diff options
Diffstat (limited to 'drivers')
243 files changed, 9585 insertions, 4277 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index e1284b8..3270d3c 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -908,9 +908,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->cap._DDC = 1; } - if (acpi_video_init_brightness(device)) - return; - if (acpi_video_backlight_support()) { struct backlight_properties props; struct pci_dev *pdev; @@ -920,6 +917,9 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) static int count = 0; char *name; + result = acpi_video_init_brightness(device); + if (result) + return; name = kasprintf(GFP_KERNEL, "acpi_video%d", count); if (!name) return; @@ -979,11 +979,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); - } else { - /* Remove the brightness object. */ - kfree(device->brightness->levels); - kfree(device->brightness); - device->brightness = NULL; } } diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 1c41722..20fd337 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -289,24 +289,24 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) /* Disable sending Early R_OK. * With "cached read" HDD testing and multiple ports busy on a SATA - * host controller, 3726 PMP will very rarely drop a deferred + * host controller, 3x26 PMP will very rarely drop a deferred * R_OK that was intended for the host. Symptom will be all * 5 drives under test will timeout, get reset, and recover. */ - if (vendor == 0x1095 && devid == 0x3726) { + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { u32 reg; err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); if (err_mask) { rc = -EIO; - reason = "failed to read Sil3726 Private Register"; + reason = "failed to read Sil3x26 Private Register"; goto fail; } reg &= ~0x1; err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); if (err_mask) { rc = -EIO; - reason = "failed to write Sil3726 Private Register"; + reason = "failed to write Sil3x26 Private Register"; goto fail; } } @@ -383,8 +383,8 @@ static void sata_pmp_quirks(struct ata_port *ap) u16 devid = sata_pmp_gscr_devid(gscr); struct ata_link *link; - if (vendor == 0x1095 && devid == 0x3726) { - /* sil3726 quirks */ + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + /* sil3x26 quirks */ ata_for_each_link(link, ap, EDGE) { /* link reports offline after LPM */ link->flags |= ATA_LFLAG_NO_LPM; diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 19720a0..851bd3f 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -293,6 +293,7 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, { struct sata_fsl_host_priv *host_priv = host->private_data; void __iomem *hcr_base = host_priv->hcr_base; + unsigned long flags; if (count > ICC_MAX_INT_COUNT_THRESHOLD) count = ICC_MAX_INT_COUNT_THRESHOLD; @@ -305,12 +306,12 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, (count > ICC_MIN_INT_COUNT_THRESHOLD)) ticks = ICC_SAFE_INT_TICKS; - spin_lock(&host->lock); + spin_lock_irqsave(&host->lock, flags); iowrite32((count << 24 | ticks), hcr_base + ICC); intr_coalescing_count = count; intr_coalescing_ticks = ticks; - spin_unlock(&host->lock); + spin_unlock_irqrestore(&host->lock, flags); DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n", intr_coalescing_count, intr_coalescing_ticks); diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index d047d92..e9a4f46 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -86,11 +86,11 @@ struct ecx_plat_data { #define SGPIO_SIGNALS 3 #define ECX_ACTIVITY_BITS 0x300000 -#define ECX_ACTIVITY_SHIFT 2 +#define ECX_ACTIVITY_SHIFT 0 #define ECX_LOCATE_BITS 0x80000 #define ECX_LOCATE_SHIFT 1 #define ECX_FAULT_BITS 0x400000 -#define ECX_FAULT_SHIFT 0 +#define ECX_FAULT_SHIFT 2 static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port, u32 shift) { diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2b7813e..ec386ee 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -141,6 +141,8 @@ static ssize_t show_mem_removable(struct device *dev, container_of(dev, struct memory_block, dev); for (i = 0; i < sections_per_block; i++) { + if (!present_section_nr(mem->start_section_nr + i)) + continue; pfn = section_nr_to_pfn(mem->start_section_nr + i); ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 29c8316..57f7778 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -128,9 +128,6 @@ struct regmap { void *cache; u32 cache_dirty; - unsigned long *cache_present; - unsigned int cache_present_nbits; - struct reg_default *patch; int patch_regs; @@ -203,6 +200,7 @@ int regcache_write(struct regmap *map, unsigned int reg, unsigned int value); int regcache_sync(struct regmap *map); int regcache_sync_block(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end); @@ -218,16 +216,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val); int regcache_lookup_reg(struct regmap *map, unsigned int reg); -int regcache_set_reg_present(struct regmap *map, unsigned int reg); - -static inline bool regcache_reg_present(struct regmap *map, unsigned int reg) -{ - if (!map->cache_present) - return true; - if (reg > map->cache_present_nbits) - return false; - return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg); -} int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len, bool async); diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 5c1435c..930cad4 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -29,6 +29,8 @@ struct regcache_rbtree_node { unsigned int base_reg; /* block of adjacent registers */ void *block; + /* Which registers are present */ + long *cache_present; /* number of registers available in the block */ unsigned int blklen; } __attribute__ ((packed)); @@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int idx, unsigned int val) { + set_bit(idx, rbnode->cache_present); regcache_set_val(map, rbnode->block, idx, val); } @@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored) map->lock(map->lock_arg); mem_size = sizeof(*rbtree_ctx); - mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); mem_size += sizeof(*n); mem_size += (n->blklen * map->cache_word_size); + mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long); regcache_rbtree_get_base_top_reg(map, n, &base, &top); this_registers = ((top - base) / map->reg_stride) + 1; @@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map) rbtree_node = rb_entry(next, struct regcache_rbtree_node, node); next = rb_next(&rbtree_node->node); rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node->cache_present); kfree(rbtree_node->block); kfree(rbtree_node); } @@ -265,7 +269,7 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; - if (!regcache_reg_present(map, reg)) + if (!test_bit(reg_tmp, rbnode->cache_present)) return -ENOENT; *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); } else { @@ -278,27 +282,45 @@ static int regcache_rbtree_read(struct regmap *map, static int regcache_rbtree_insert_to_block(struct regmap *map, struct regcache_rbtree_node *rbnode, - unsigned int pos, unsigned int reg, + unsigned int base_reg, + unsigned int top_reg, + unsigned int reg, unsigned int value) { + unsigned int blklen; + unsigned int pos, offset; + unsigned long *present; u8 *blk; + blklen = (top_reg - base_reg) / map->reg_stride + 1; + pos = (reg - base_reg) / map->reg_stride; + offset = (rbnode->base_reg - base_reg) / map->reg_stride; + blk = krealloc(rbnode->block, - (rbnode->blklen + 1) * map->cache_word_size, + blklen * map->cache_word_size, GFP_KERNEL); if (!blk) return -ENOMEM; + present = krealloc(rbnode->cache_present, + BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL); + if (!present) { + kfree(blk); + return -ENOMEM; + } + /* insert the register value in the correct place in the rbnode block */ - memmove(blk + (pos + 1) * map->cache_word_size, - blk + pos * map->cache_word_size, - (rbnode->blklen - pos) * map->cache_word_size); + if (pos == 0) { + memmove(blk + offset * map->cache_word_size, + blk, rbnode->blklen * map->cache_word_size); + bitmap_shift_right(present, present, offset, blklen); + } /* update the rbnode block, its size and the base register */ rbnode->block = blk; - rbnode->blklen++; - if (!pos) - rbnode->base_reg = reg; + rbnode->blklen = blklen; + rbnode->base_reg = base_reg; + rbnode->cache_present = present; regcache_rbtree_set_register(map, rbnode, pos, value); return 0; @@ -325,25 +347,34 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) if (i != map->rd_table->n_yes_ranges) { range = &map->rd_table->yes_ranges[i]; - rbnode->blklen = range->range_max - range->range_min - + 1; + rbnode->blklen = (range->range_max - range->range_min) / + map->reg_stride + 1; rbnode->base_reg = range->range_min; } } if (!rbnode->blklen) { - rbnode->blklen = sizeof(*rbnode); + rbnode->blklen = 1; rbnode->base_reg = reg; } rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); - if (!rbnode->block) { - kfree(rbnode); - return NULL; - } + if (!rbnode->block) + goto err_free; + + rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) * + sizeof(*rbnode->cache_present), GFP_KERNEL); + if (!rbnode->cache_present) + goto err_free_block; return rbnode; + +err_free_block: + kfree(rbnode->block); +err_free: + kfree(rbnode); + return NULL; } static int regcache_rbtree_write(struct regmap *map, unsigned int reg, @@ -353,15 +384,9 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, struct regcache_rbtree_node *rbnode, *rbnode_tmp; struct rb_node *node; unsigned int reg_tmp; - unsigned int pos; - int i; int ret; rbtree_ctx = map->cache; - /* update the reg_present bitmap, make space if necessary */ - ret = regcache_set_reg_present(map, reg); - if (ret < 0) - return ret; /* if we can't locate it in the cached rbnode we'll have * to traverse the rbtree looking for it. @@ -371,30 +396,43 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; regcache_rbtree_set_register(map, rbnode, reg_tmp, value); } else { + unsigned int base_reg, top_reg; + unsigned int new_base_reg, new_top_reg; + unsigned int min, max; + unsigned int max_dist; + + max_dist = map->reg_stride * sizeof(*rbnode_tmp) / + map->cache_word_size; + if (reg < max_dist) + min = 0; + else + min = reg - max_dist; + max = reg + max_dist; + /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); - for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + - (i * map->reg_stride); - if (abs(reg_tmp - reg) != map->reg_stride) - continue; - /* decide where in the block to place our register */ - if (reg_tmp + map->reg_stride == reg) - pos = i + 1; - else - pos = i; - ret = regcache_rbtree_insert_to_block(map, - rbnode_tmp, - pos, reg, - value); - if (ret) - return ret; - rbtree_ctx->cached_rbnode = rbnode_tmp; - return 0; + + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, + &base_reg, &top_reg); + + if (base_reg <= max && top_reg >= min) { + new_base_reg = min(reg, base_reg); + new_top_reg = max(reg, top_reg); + } else { + continue; } + + ret = regcache_rbtree_insert_to_block(map, rbnode_tmp, + new_base_reg, + new_top_reg, reg, + value); + if (ret) + return ret; + rbtree_ctx->cached_rbnode = rbnode_tmp; + return 0; } /* We did not manage to find a place to insert it in @@ -418,30 +456,34 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, struct regcache_rbtree_ctx *rbtree_ctx; struct rb_node *node; struct regcache_rbtree_node *rbnode; + unsigned int base_reg, top_reg; + unsigned int start, end; int ret; - int base, end; rbtree_ctx = map->cache; for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { rbnode = rb_entry(node, struct regcache_rbtree_node, node); - if (rbnode->base_reg > max) + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); + if (base_reg > max) break; - if (rbnode->base_reg + rbnode->blklen < min) + if (top_reg < min) continue; - if (min > rbnode->base_reg) - base = min - rbnode->base_reg; + if (min > base_reg) + start = (min - base_reg) / map->reg_stride; else - base = 0; + start = 0; - if (max < rbnode->base_reg + rbnode->blklen) - end = max - rbnode->base_reg + 1; + if (max < top_reg) + end = (max - base_reg) / map->reg_stride + 1; else end = rbnode->blklen; - ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg, - base, end); + ret = regcache_sync_block(map, rbnode->block, + rbnode->cache_present, + rbnode->base_reg, start, end); if (ret != 0) return ret; } @@ -449,6 +491,42 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, return regmap_async_complete(map); } +static int regcache_rbtree_drop(struct regmap *map, unsigned int min, + unsigned int max) +{ + struct regcache_rbtree_ctx *rbtree_ctx; + struct regcache_rbtree_node *rbnode; + struct rb_node *node; + unsigned int base_reg, top_reg; + unsigned int start, end; + + rbtree_ctx = map->cache; + for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { + rbnode = rb_entry(node, struct regcache_rbtree_node, node); + + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); + if (base_reg > max) + break; + if (top_reg < min) + continue; + + if (min > base_reg) + start = (min - base_reg) / map->reg_stride; + else + start = 0; + + if (max < top_reg) + end = (max - base_reg) / map->reg_stride + 1; + else + end = rbnode->blklen; + + bitmap_clear(rbnode->cache_present, start, end - start); + } + + return 0; +} + struct regcache_ops regcache_rbtree_ops = { .type = REGCACHE_RBTREE, .name = "rbtree", @@ -456,5 +534,6 @@ struct regcache_ops regcache_rbtree_ops = { .exit = regcache_rbtree_exit, .read = regcache_rbtree_read, .write = regcache_rbtree_write, - .sync = regcache_rbtree_sync + .sync = regcache_rbtree_sync, + .drop = regcache_rbtree_drop, }; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 3455f83..d6c2d69 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -121,8 +121,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) map->reg_defaults_raw = config->reg_defaults_raw; map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8); map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw; - map->cache_present = NULL; - map->cache_present_nbits = 0; map->cache = NULL; map->cache_ops = cache_types[i]; @@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map) BUG_ON(!map->cache_ops); - kfree(map->cache_present); kfree(map->reg_defaults); if (map->cache_free) kfree(map->reg_defaults_raw); @@ -241,9 +238,6 @@ int regcache_write(struct regmap *map, BUG_ON(!map->cache_ops); - if (!regmap_writeable(map, reg)) - return -EIO; - if (!regmap_volatile(map, reg)) return map->cache_ops->write(map, reg, value); @@ -410,22 +404,16 @@ EXPORT_SYMBOL_GPL(regcache_sync_region); int regcache_drop_region(struct regmap *map, unsigned int min, unsigned int max) { - unsigned int reg; int ret = 0; - if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) + if (!map->cache_ops || !map->cache_ops->drop) return -EINVAL; map->lock(map->lock_arg); trace_regcache_drop_region(map->dev, min, max); - if (map->cache_present) - for (reg = min; reg < max + 1; reg++) - clear_bit(reg, map->cache_present); - - if (map->cache_ops && map->cache_ops->drop) - ret = map->cache_ops->drop(map, min, max); + ret = map->cache_ops->drop(map, min, max); map->unlock(map->lock_arg); @@ -493,42 +481,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable) } EXPORT_SYMBOL_GPL(regcache_cache_bypass); -int regcache_set_reg_present(struct regmap *map, unsigned int reg) -{ - unsigned long *cache_present; - unsigned int cache_present_size; - unsigned int nregs; - int i; - - nregs = reg + 1; - cache_present_size = BITS_TO_LONGS(nregs); - cache_present_size *= sizeof(long); - - if (!map->cache_present) { - cache_present = kmalloc(cache_present_size, GFP_KERNEL); - if (!cache_present) - return -ENOMEM; - bitmap_zero(cache_present, nregs); - map->cache_present = cache_present; - map->cache_present_nbits = nregs; - } - - if (nregs > map->cache_present_nbits) { - cache_present = krealloc(map->cache_present, - cache_present_size, GFP_KERNEL); - if (!cache_present) - return -ENOMEM; - for (i = 0; i < nregs; i++) - if (i >= map->cache_present_nbits) - clear_bit(i, cache_present); - map->cache_present = cache_present; - map->cache_present_nbits = nregs; - } - - set_bit(reg, map->cache_present); - return 0; -} - bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val) { @@ -620,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) return -ENOENT; } +static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx) +{ + if (!cache_present) + return true; + + return test_bit(idx, cache_present); +} + static int regcache_sync_block_single(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -630,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) + if (!regcache_reg_present(cache_present, i)) continue; val = regcache_get_val(map, block, i); @@ -681,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, } static int regcache_sync_block_raw(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { @@ -693,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(map, regtmp)) { + if (!regcache_reg_present(cache_present, i)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) @@ -724,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, } int regcache_sync_block(struct regmap *map, void *block, + unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end) { if (regmap_can_raw_write(map)) - return regcache_sync_block_raw(map, block, block_base, - start, end); + return regcache_sync_block_raw(map, block, cache_present, + block_base, start, end); else - return regcache_sync_block_single(map, block, block_base, - start, end); + return regcache_sync_block_single(map, block, cache_present, + block_base, start, end); } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 5349575..6c2652a 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -85,8 +85,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, unsigned int reg_offset; /* Suppress the cache if we're using a subrange */ - if (from) - return from; + if (base) + return base; /* * If we don't have a cache build one so we don't have to do a diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1643e88..d10456f 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -418,6 +418,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, reg, ret); goto err_alloc; } + + if (!chip->init_ack_masked) + continue; + + /* Ack masked but set interrupts */ + reg = chip->status_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_read(map, reg, &d->status_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); + goto err_alloc; + } + + if (d->status_buf[i] && chip->ack_base) { + reg = chip->ack_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_write(map, reg, + d->status_buf[i] & d->mask_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to ack 0x%x: %d\n", + reg, ret); + goto err_alloc; + } + } } /* Wake is disabled by default */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e0d0c7d..7d689a1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -303,6 +303,7 @@ static void regmap_unlock_mutex(void *__map) } static void regmap_lock_spinlock(void *__map) +__acquires(&map->spinlock) { struct regmap *map = __map; unsigned long flags; @@ -312,6 +313,7 @@ static void regmap_lock_spinlock(void *__map) } static void regmap_unlock_spinlock(void *__map) +__releases(&map->spinlock) { struct regmap *map = __map; spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); @@ -687,6 +689,10 @@ skip_format_initialization: unsigned win_max = win_min + config->ranges[j].window_len - 1; + /* Allow data window inside its own virtual range */ + if (j == i) + continue; + if (range_cfg->range_min <= sel_reg && sel_reg <= range_cfg->range_max) { dev_err(map->dev, @@ -1261,6 +1267,9 @@ int _regmap_write(struct regmap *map, unsigned int reg, int ret; void *context = _regmap_map_get_context(map); + if (!regmap_writeable(map, reg)) + return -EIO; + if (!map->cache_bypass && !map->defer_caching) { ret = regcache_write(map, reg, val); if (ret != 0) @@ -1888,13 +1897,10 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) { + struct reg_default *p; int i, ret; bool bypass; - /* If needed the implementation can be extended to support this */ - if (map->patch) - return -EBUSY; - map->lock(map->lock_arg); bypass = map->cache_bypass; @@ -1911,11 +1917,13 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, } } - map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL); - if (map->patch != NULL) { - memcpy(map->patch, regs, - num_regs * sizeof(struct reg_default)); - map->patch_regs = num_regs; + p = krealloc(map->patch, + sizeof(struct reg_default) * (map->patch_regs + num_regs), + GFP_KERNEL); + if (p) { + memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs)); + map->patch = p; + map->patch_regs += num_regs; } else { ret = -ENOMEM; } diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index ad1fde2..e9dedb2 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -197,7 +197,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) cpu_dev = &pdev->dev; cpu_dev->of_node = np; - cpu_reg = devm_regulator_get(cpu_dev, "cpu0"); + cpu_reg = devm_regulator_get_optional(cpu_dev, "cpu0"); if (IS_ERR(cpu_reg)) { /* * If cpu0 regulator supply node is present, but regulator is diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6825957..643d7c7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -194,7 +194,7 @@ config SIRF_DMA Enable support for the CSR SiRFprimaII DMA engine. config TI_EDMA - tristate "TI EDMA support" + bool "TI EDMA support" depends on ARCH_DAVINCI || ARCH_OMAP select DMA_ENGINE select DMA_VIRTUAL_CHANNELS diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 19e3660..3bc84145 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -500,7 +500,8 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, &status)) goto log_fail; - while (status == SDVO_CMD_STATUS_PENDING && retry--) { + while ((status == SDVO_CMD_STATUS_PENDING || + status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) { udelay(15); if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, SDVO_I2C_CMD_STATUS, diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index dc53a52..9e65783 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -85,9 +85,17 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) { + struct drm_i915_gem_object *obj = attachment->dmabuf->priv; + + mutex_lock(&obj->base.dev->struct_mutex); + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); sg_free_table(sg); kfree(sg); + + i915_gem_object_unpin_pages(obj); + + mutex_unlock(&obj->base.dev->struct_mutex); } static void i915_gem_dmabuf_release(struct dma_buf *dma_buf) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6f51429..342f1f3 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -752,6 +752,8 @@ will not assert AGPBUSY# and will only be delivered when out of C3. */ #define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */ +#define INSTPM_TLB_INVALIDATE (1<<9) +#define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc @@ -4438,7 +4440,7 @@ #define EDP_LINK_TRAIN_600MV_0DB_IVB (0x30 <<22) #define EDP_LINK_TRAIN_600MV_3_5DB_IVB (0x36 <<22) #define EDP_LINK_TRAIN_800MV_0DB_IVB (0x38 <<22) -#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x33 <<22) +#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x3e <<22) /* legacy values */ #define EDP_LINK_TRAIN_500MV_0DB_IVB (0x00 <<22) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e38b457..be79f47 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10042,6 +10042,8 @@ struct intel_display_error_state { u32 power_well_driver; + int num_transcoders; + struct intel_cursor_error_state { u32 control; u32 position; @@ -10050,16 +10052,7 @@ struct intel_display_error_state { } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { - enum transcoder cpu_transcoder; - u32 conf; u32 source; - - u32 htotal; - u32 hblank; - u32 hsync; - u32 vtotal; - u32 vblank; - u32 vsync; } pipe[I915_MAX_PIPES]; struct intel_plane_error_state { @@ -10071,6 +10064,19 @@ struct intel_display_error_state { u32 surface; u32 tile_offset; } plane[I915_MAX_PIPES]; + + struct intel_transcoder_error_state { + enum transcoder cpu_transcoder; + + u32 conf; + + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + } transcoder[4]; }; struct intel_display_error_state * @@ -10078,9 +10084,17 @@ intel_display_capture_error_state(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct intel_display_error_state *error; - enum transcoder cpu_transcoder; + int transcoders[] = { + TRANSCODER_A, + TRANSCODER_B, + TRANSCODER_C, + TRANSCODER_EDP, + }; int i; + if (INTEL_INFO(dev)->num_pipes == 0) + return NULL; + error = kmalloc(sizeof(*error), GFP_ATOMIC); if (error == NULL) return NULL; @@ -10089,9 +10103,6 @@ intel_display_capture_error_state(struct drm_device *dev) error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); for_each_pipe(i) { - cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i); - error->pipe[i].cpu_transcoder = cpu_transcoder; - if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) { error->cursor[i].control = I915_READ(CURCNTR(i)); error->cursor[i].position = I915_READ(CURPOS(i)); @@ -10115,14 +10126,25 @@ intel_display_capture_error_state(struct drm_device *dev) error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i)); } - error->pipe[i].conf = I915_READ(PIPECONF(cpu_transcoder)); error->pipe[i].source = I915_READ(PIPESRC(i)); - error->pipe[i].htotal = I915_READ(HTOTAL(cpu_transcoder)); - error->pipe[i].hblank = I915_READ(HBLANK(cpu_transcoder)); - error->pipe[i].hsync = I915_READ(HSYNC(cpu_transcoder)); - error->pipe[i].vtotal = I915_READ(VTOTAL(cpu_transcoder)); - error->pipe[i].vblank = I915_READ(VBLANK(cpu_transcoder)); - error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder)); + } + + error->num_transcoders = INTEL_INFO(dev)->num_pipes; + if (HAS_DDI(dev_priv->dev)) + error->num_transcoders++; /* Account for eDP. */ + + for (i = 0; i < error->num_transcoders; i++) { + enum transcoder cpu_transcoder = transcoders[i]; + + error->transcoder[i].cpu_transcoder = cpu_transcoder; + + error->transcoder[i].conf = I915_READ(PIPECONF(cpu_transcoder)); + error->transcoder[i].htotal = I915_READ(HTOTAL(cpu_transcoder)); + error->transcoder[i].hblank = I915_READ(HBLANK(cpu_transcoder)); + error->transcoder[i].hsync = I915_READ(HSYNC(cpu_transcoder)); + error->transcoder[i].vtotal = I915_READ(VTOTAL(cpu_transcoder)); + error->transcoder[i].vblank = I915_READ(VBLANK(cpu_transcoder)); + error->transcoder[i].vsync = I915_READ(VSYNC(cpu_transcoder)); } /* In the code above we read the registers without checking if the power @@ -10144,22 +10166,16 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, { int i; + if (!error) + return; + err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); if (HAS_POWER_WELL(dev)) err_printf(m, "PWR_WELL_CTL2: %08x\n", error->power_well_driver); for_each_pipe(i) { err_printf(m, "Pipe [%d]:\n", i); - err_printf(m, " CPU transcoder: %c\n", - transcoder_name(error->pipe[i].cpu_transcoder)); - err_printf(m, " CONF: %08x\n", error->pipe[i].conf); err_printf(m, " SRC: %08x\n", error->pipe[i].source); - err_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); - err_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank); - err_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync); - err_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal); - err_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank); - err_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync); err_printf(m, "Plane [%d]:\n", i); err_printf(m, " CNTR: %08x\n", error->plane[i].control); @@ -10180,5 +10196,17 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, err_printf(m, " POS: %08x\n", error->cursor[i].position); err_printf(m, " BASE: %08x\n", error->cursor[i].base); } + + for (i = 0; i < error->num_transcoders; i++) { + err_printf(m, " CPU transcoder: %c\n", + transcoder_name(error->transcoder[i].cpu_transcoder)); + err_printf(m, " CONF: %08x\n", error->transcoder[i].conf); + err_printf(m, " HTOTAL: %08x\n", error->transcoder[i].htotal); + err_printf(m, " HBLANK: %08x\n", error->transcoder[i].hblank); + err_printf(m, " HSYNC: %08x\n", error->transcoder[i].hsync); + err_printf(m, " VTOTAL: %08x\n", error->transcoder[i].vtotal); + err_printf(m, " VBLANK: %08x\n", error->transcoder[i].vblank); + err_printf(m, " VSYNC: %08x\n", error->transcoder[i].vsync); + } } #endif diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 664118d..079ef01 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -968,6 +968,18 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) I915_WRITE(mmio, (u32)ring->status_page.gfx_addr); POSTING_READ(mmio); + + /* Flush the TLB for this page */ + if (INTEL_INFO(dev)->gen >= 6) { + u32 reg = RING_INSTPM(ring->mmio_base); + I915_WRITE(reg, + _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE | + INSTPM_SYNC_FLUSH)); + if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0, + 1000)) + DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n", + ring->name); + } } static int diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index d829172..7a4e089 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -98,6 +98,8 @@ nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, u32 splitoff; u32 s, e; + BUG_ON(!type); + list_for_each_entry(this, &mm->free, fl_entry) { e = this->offset + this->length; s = this->offset; @@ -162,6 +164,8 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, struct nouveau_mm_node *prev, *this, *next; u32 mask = align - 1; + BUG_ON(!type); + list_for_each_entry_reverse(this, &mm->free, fl_entry) { u32 e = this->offset + this->length; u32 s = this->offset; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h index d550226..9d2cd20 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h @@ -20,8 +20,8 @@ nouveau_mc(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC]; } -#define nouveau_mc_create(p,e,o,d) \ - nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_mc_create(p,e,o,m,d) \ + nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d) #define nouveau_mc_destroy(p) ({ \ struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \ }) @@ -33,7 +33,8 @@ nouveau_mc(void *obj) }) int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int, void **); + struct nouveau_oclass *, const struct nouveau_mc_intr *, + int, void **); void _nouveau_mc_dtor(struct nouveau_object *); int _nouveau_mc_init(struct nouveau_object *); int _nouveau_mc_fini(struct nouveau_object *, bool); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c index 19e3a9a..ab7ef0a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c @@ -40,15 +40,15 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return ret; switch (pfb914 & 0x00000003) { - case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break; - case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break; - case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break; case 0x00000003: break; } - pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - pfb->ram->tags = nv_rd32(pfb, 0x100320); + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c index 7192aa6..63a6aab 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c @@ -38,8 +38,8 @@ nv4e_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram->type = NV_MEM_TYPE_STOLEN; + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->type = NV_MEM_TYPE_STOLEN; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c index bcca883..cce65cc 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c @@ -30,8 +30,9 @@ struct nvc0_ltcg_priv { struct nouveau_ltcg base; u32 part_nr; u32 subp_nr; - struct nouveau_mm tags; u32 num_tags; + u32 tag_base; + struct nouveau_mm tags; struct nouveau_mm_node *tag_ram; }; @@ -117,10 +118,6 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) u32 tag_size, tag_margin, tag_align; int ret; - nv_wr32(priv, 0x17e8d8, priv->part_nr); - if (nv_device(pfb)->card_type >= NV_E0) - nv_wr32(priv, 0x17e000, priv->part_nr); - /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */ priv->num_tags = (pfb->ram->size >> 17) / 4; if (priv->num_tags > (1 << 17)) @@ -142,7 +139,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) tag_size += tag_align; tag_size = (tag_size + 0xfff) >> 12; /* round up */ - ret = nouveau_mm_tail(&pfb->vram, 0, tag_size, tag_size, 1, + ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1, &priv->tag_ram); if (ret) { priv->num_tags = 0; @@ -152,7 +149,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) tag_base += tag_align - 1; ret = do_div(tag_base, tag_align); - nv_wr32(priv, 0x17e8d4, tag_base); + priv->tag_base = tag_base; } ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1); @@ -182,8 +179,6 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, } priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28; - nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ - ret = nvc0_ltcg_init_tag_ram(pfb, priv); if (ret) return ret; @@ -209,13 +204,32 @@ nvc0_ltcg_dtor(struct nouveau_object *object) nouveau_ltcg_destroy(ltcg); } +static int +nvc0_ltcg_init(struct nouveau_object *object) +{ + struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object; + struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg; + int ret; + + ret = nouveau_ltcg_init(ltcg); + if (ret) + return ret; + + nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ + nv_wr32(priv, 0x17e8d8, priv->part_nr); + if (nv_device(ltcg)->card_type >= NV_E0) + nv_wr32(priv, 0x17e000, priv->part_nr); + nv_wr32(priv, 0x17e8d4, priv->tag_base); + return 0; +} + struct nouveau_oclass nvc0_ltcg_oclass = { .handle = NV_SUBDEV(LTCG, 0xc0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_ltcg_ctor, .dtor = nvc0_ltcg_dtor, - .init = _nouveau_ltcg_init, + .init = nvc0_ltcg_init, .fini = _nouveau_ltcg_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c index 1c0330b..ec9cd6f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c @@ -80,7 +80,9 @@ _nouveau_mc_dtor(struct nouveau_object *object) int nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, int length, void **pobject) + struct nouveau_oclass *oclass, + const struct nouveau_mc_intr *intr_map, + int length, void **pobject) { struct nouveau_device *device = nv_device(parent); struct nouveau_mc *pmc; @@ -92,6 +94,8 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + pmc->intr_map = intr_map; + ret = request_irq(device->pdev->irq, nouveau_mc_intr, IRQF_SHARED, "nouveau", pmc); if (ret < 0) diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c index 8c76971..64aa4ed 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c @@ -50,12 +50,11 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv04_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c index 5191937..d989178 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c @@ -36,12 +36,11 @@ nv44_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv44_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index f25fc5f..2b1afe2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -53,12 +53,11 @@ nv50_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv50_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index e82fd21..0d57b4d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -54,12 +54,11 @@ nv98_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv98_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv98_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index c5da3ba..104175c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -57,12 +57,11 @@ nvc0_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nvc0_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 0782bd2..6a13ffb 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -606,6 +606,24 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) regp->ramdac_a34 = 0x1; } +static int +nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); + if (ret == 0) { + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(nvfb->nvbo, &disp->image[nv_crtc->index]); + } + + return ret; +} + /** * Sets up registers for the given mode/adjusted_mode pair. * @@ -622,10 +640,15 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_device *dev = crtc->dev; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_drm *drm = nouveau_drm(dev); + int ret; NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); drm_mode_debug_printmodeline(adjusted_mode); + ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); @@ -722,6 +745,7 @@ static void nv_crtc_commit(struct drm_crtc *crtc) static void nv_crtc_destroy(struct drm_crtc *crtc) { + struct nv04_display *disp = nv04_display(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); if (!nv_crtc) @@ -729,6 +753,10 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); + nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); @@ -754,6 +782,16 @@ nv_crtc_gamma_load(struct drm_crtc *crtc) } static void +nv_crtc_disable(struct drm_crtc *crtc) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); +} + +static void nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, uint32_t size) { @@ -791,7 +829,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_framebuffer *drm_fb; struct nouveau_framebuffer *fb; int arb_burst, arb_lwm; - int ret; NV_DEBUG(drm, "index %d\n", nv_crtc->index); @@ -801,10 +838,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, return 0; } - /* If atomic, we want to switch to the fb we were passed, so - * now we update pointers to do that. (We don't pin; just - * assume we're already pinned and update the base address.) + * now we update pointers to do that. */ if (atomic) { drm_fb = passed_fb; @@ -812,17 +847,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, } else { drm_fb = crtc->fb; fb = nouveau_framebuffer(crtc->fb); - /* If not atomic, we can go ahead and pin, and unpin the - * old fb we were passed. - */ - ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); - if (ret) - return ret; - - if (passed_fb) { - struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb); - nouveau_bo_unpin(ofb->nvbo); - } } nv_crtc->fb.offset = fb->nvbo->bo.offset; @@ -877,6 +901,9 @@ static int nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + int ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); } @@ -1027,6 +1054,7 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { .mode_set_base = nv04_crtc_mode_set_base, .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, .load_lut = nv_crtc_gamma_load, + .disable = nv_crtc_disable, }; int diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index a0a031d..9928187 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -81,6 +81,7 @@ struct nv04_display { uint32_t saved_vga_font[4][16384]; uint32_t dac_users[4]; struct nouveau_object *core; + struct nouveau_bo *image[2]; }; static inline struct nv04_display * diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 907d20e..a03e75d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -577,6 +577,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, ret = nv50_display_flip_next(crtc, fb, chan, 0); if (ret) goto fail_unreserve; + } else { + struct nv04_display *dispnv04 = nv04_display(dev); + nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]); } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 3af5bcd..625f80d 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -131,7 +131,7 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll, if (clk < pll->vco1.max_freq) pll->vco2.max_freq = 0; - pclk->pll_calc(pclk, pll, clk, &coef); + ret = pclk->pll_calc(pclk, pll, clk, &coef); if (ret == 0) return -ERANGE; diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 274b8e1..9f19259 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2163,7 +2163,7 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v); WREG32(reg, tmp_); \ } while (0) #define WREG32_AND(reg, and) WREG32_P(reg, 0, and) -#define WREG32_OR(reg, or) WREG32_P(reg, or, ~or) +#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) #define WREG32_PLL_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32_PLL(reg); \ diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index f1c1575..b79f4f5 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -356,6 +356,14 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, return -EINVAL; } + if (bo->tbo.sync_obj) { + r = radeon_fence_wait(bo->tbo.sync_obj, false); + if (r) { + DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); + return r; + } + } + r = radeon_bo_kmap(bo, &ptr); if (r) { DRM_ERROR("Failed mapping the UVD message (%d)!\n", r); diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index bcc68ec..f5e92cf 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -744,10 +744,10 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv730_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv730_golden_registers)); radeon_program_register_sequence(rdev, rv730_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv730_mgcg_init)); break; case CHIP_RV710: radeon_program_register_sequence(rdev, @@ -758,18 +758,18 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv710_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv710_golden_registers)); radeon_program_register_sequence(rdev, rv710_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv710_mgcg_init)); break; case CHIP_RV740: radeon_program_register_sequence(rdev, rv740_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv740_golden_registers)); radeon_program_register_sequence(rdev, rv740_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv740_mgcg_init)); break; default: break; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 3751730..1a0bf07 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -29,7 +29,9 @@ #include <drm/drmP.h> #include <drm/ttm/ttm_bo_driver.h> -#define VMW_PPN_SIZE sizeof(unsigned long) +#define VMW_PPN_SIZE (sizeof(unsigned long)) +/* A future safe maximum remap size. */ +#define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) static int vmw_gmr2_bind(struct vmw_private *dev_priv, struct page *pages[], @@ -38,43 +40,61 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, { SVGAFifoCmdDefineGMR2 define_cmd; SVGAFifoCmdRemapGMR2 remap_cmd; - uint32_t define_size = sizeof(define_cmd) + 4; - uint32_t remap_size = VMW_PPN_SIZE * num_pages + sizeof(remap_cmd) + 4; uint32_t *cmd; uint32_t *cmd_orig; + uint32_t define_size = sizeof(define_cmd) + sizeof(*cmd); + uint32_t remap_num = num_pages / VMW_PPN_PER_REMAP + ((num_pages % VMW_PPN_PER_REMAP) > 0); + uint32_t remap_size = VMW_PPN_SIZE * num_pages + (sizeof(remap_cmd) + sizeof(*cmd)) * remap_num; + uint32_t remap_pos = 0; + uint32_t cmd_size = define_size + remap_size; uint32_t i; - cmd_orig = cmd = vmw_fifo_reserve(dev_priv, define_size + remap_size); + cmd_orig = cmd = vmw_fifo_reserve(dev_priv, cmd_size); if (unlikely(cmd == NULL)) return -ENOMEM; define_cmd.gmrId = gmr_id; define_cmd.numPages = num_pages; + *cmd++ = SVGA_CMD_DEFINE_GMR2; + memcpy(cmd, &define_cmd, sizeof(define_cmd)); + cmd += sizeof(define_cmd) / sizeof(*cmd); + + /* + * Need to split the command if there are too many + * pages that goes into the gmr. + */ + remap_cmd.gmrId = gmr_id; remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ? SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32; - remap_cmd.offsetPages = 0; - remap_cmd.numPages = num_pages; - *cmd++ = SVGA_CMD_DEFINE_GMR2; - memcpy(cmd, &define_cmd, sizeof(define_cmd)); - cmd += sizeof(define_cmd) / sizeof(uint32); + while (num_pages > 0) { + unsigned long nr = min(num_pages, (unsigned long)VMW_PPN_PER_REMAP); + + remap_cmd.offsetPages = remap_pos; + remap_cmd.numPages = nr; - *cmd++ = SVGA_CMD_REMAP_GMR2; - memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); - cmd += sizeof(remap_cmd) / sizeof(uint32); + *cmd++ = SVGA_CMD_REMAP_GMR2; + memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); + cmd += sizeof(remap_cmd) / sizeof(*cmd); - for (i = 0; i < num_pages; ++i) { - if (VMW_PPN_SIZE <= 4) - *cmd = page_to_pfn(*pages++); - else - *((uint64_t *)cmd) = page_to_pfn(*pages++); + for (i = 0; i < nr; ++i) { + if (VMW_PPN_SIZE <= 4) + *cmd = page_to_pfn(*pages++); + else + *((uint64_t *)cmd) = page_to_pfn(*pages++); - cmd += VMW_PPN_SIZE / sizeof(*cmd); + cmd += VMW_PPN_SIZE / sizeof(*cmd); + } + + num_pages -= nr; + remap_pos += nr; } - vmw_fifo_commit(dev_priv, define_size + remap_size); + BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd)); + + vmw_fifo_commit(dev_priv, cmd_size); return 0; } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e989f7f..b3ab9d4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -296,8 +296,8 @@ config SENSORS_K10TEMP If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of the AMD Family 10h and all revisions of the AMD Family 11h, - 12h (Llano), 14h (Brazos) and 15h (Bulldozer/Trinity) - microarchitectures. + 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity) and + 16h (Kabini) microarchitectures. This driver can also be built as a module. If so, the module will be called k10temp. @@ -511,6 +511,16 @@ config SENSORS_HIH6130 This driver can also be built as a module. If so, the module will be called hih6130. +config SENSORS_HTU21 + tristate "Measurement Specialties HTU21D humidity/temperature sensors" + depends on I2C + help + If you say yes here you get support for the Measurement Specialties + HTU21D humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called htu21. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 @@ -1202,8 +1212,8 @@ config SENSORS_ADS1015 tristate "Texas Instruments ADS1015" depends on I2C help - If you say yes here you get support for Texas Instruments ADS1015 - 12-bit 4-input ADC device. + If you say yes here you get support for Texas Instruments + ADS1015/ADS1115 12/16-bit 4-input ADC device. This driver can also be built as a module. If so, the module will be called ads1015. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4f0fb52..ec7cde0 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o +obj-$(CONFIG_SENSORS_HTU21) += htu21.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 6351aba..a9e3d01 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -2,7 +2,7 @@ * A hwmon driver for ACPI 4.0 power meters * Copyright (C) 2009 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -1001,7 +1001,7 @@ static void __exit acpi_power_meter_exit(void) acpi_bus_unregister_driver(&acpi_power_meter_driver); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ACPI 4.0 power meter driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index 2798246..7f9dc2f 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c @@ -46,17 +46,28 @@ static const unsigned int fullscale_table[8] = { 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; /* Data rates in samples per second */ -static const unsigned int data_rate_table[8] = { - 128, 250, 490, 920, 1600, 2400, 3300, 3300 }; +static const unsigned int data_rate_table_1015[8] = { + 128, 250, 490, 920, 1600, 2400, 3300, 3300 +}; + +static const unsigned int data_rate_table_1115[8] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; #define ADS1015_DEFAULT_CHANNELS 0xff #define ADS1015_DEFAULT_PGA 2 #define ADS1015_DEFAULT_DATA_RATE 4 +enum ads1015_chips { + ads1015, + ads1115, +}; + struct ads1015_data { struct device *hwmon_dev; struct mutex update_lock; /* mutex protect updates */ struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + enum ads1015_chips id; }; static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) @@ -66,6 +77,8 @@ static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) unsigned int pga = data->channel_data[channel].pga; unsigned int data_rate = data->channel_data[channel].data_rate; unsigned int conversion_time_ms; + const unsigned int * const rate_table = data->id == ads1115 ? + data_rate_table_1115 : data_rate_table_1015; int res; mutex_lock(&data->update_lock); @@ -75,7 +88,7 @@ static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) if (res < 0) goto err_unlock; config = res; - conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]); + conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]); /* setup and start single conversion */ config &= 0x001f; @@ -113,8 +126,9 @@ static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, struct ads1015_data *data = i2c_get_clientdata(client); unsigned int pga = data->channel_data[channel].pga; int fullscale = fullscale_table[pga]; + const unsigned mask = data->id == ads1115 ? 0x7fff : 0x7ff0; - return DIV_ROUND_CLOSEST(reg * fullscale, 0x7ff0); + return DIV_ROUND_CLOSEST(reg * fullscale, mask); } /* sysfs callback function */ @@ -257,7 +271,7 @@ static int ads1015_probe(struct i2c_client *client, GFP_KERNEL); if (!data) return -ENOMEM; - + data->id = id->driver_data; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -286,7 +300,8 @@ exit_remove: } static const struct i2c_device_id ads1015_id[] = { - { "ads1015", 0 }, + { "ads1015", ads1015}, + { "ads1115", ads1115}, { } }; MODULE_DEVICE_TABLE(i2c, ads1015_id); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index ba962ac..7092c78 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -145,7 +145,7 @@ static int ads7828_remove(struct i2c_client *client) static int ads7828_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ads7828_platform_data *pdata = client->dev.platform_data; + struct ads7828_platform_data *pdata = dev_get_platdata(&client->dev); struct ads7828_data *data; int err; diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 69481d3..addb5a4 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -2,7 +2,7 @@ * A hwmon driver for the Analog Devices ADT7462 * Copyright (C) 2008 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -333,7 +333,7 @@ static int ADT7462_REG_VOLT_MAX(struct adt7462_data *data, int which) return 0x4C; break; } - return -ENODEV; + return 0; } static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) @@ -392,7 +392,7 @@ static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) return 0x77; break; } - return -ENODEV; + return 0; } static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) @@ -1970,6 +1970,6 @@ static int adt7462_remove(struct i2c_client *client) module_i2c_driver(adt7462_driver); -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ADT7462 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 6099f50..0f4dea5 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -2,7 +2,7 @@ * A hwmon driver for the Analog Devices ADT7470 * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -1314,6 +1314,6 @@ static int adt7470_remove(struct i2c_client *client) module_i2c_driver(adt7470_driver); -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ADT7470 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 2e5e2dc..78be661 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -316,6 +316,18 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) return tjmax; } +static bool cpu_has_tjmax(struct cpuinfo_x86 *c) +{ + u8 model = c->x86_model; + + return model > 0xe && + model != 0x1c && + model != 0x26 && + model != 0x27 && + model != 0x35 && + model != 0x36; +} + static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { int err; @@ -328,7 +340,7 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) */ err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); if (err) { - if (c->x86_model > 0xe && c->x86_model != 0x1c) + if (cpu_has_tjmax(c)) dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); } else { val = (eax >> 16) & 0xff; diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index f1d6b42..0918b91 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -77,7 +77,7 @@ struct ds620_data { static void ds620_init_client(struct i2c_client *client) { - struct ds620_platform_data *ds620_info = client->dev.platform_data; + struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev); u16 conf, new_conf; new_conf = conf = diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 0c9f3da..15b7f52 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1375,7 +1375,7 @@ static void f71805f_init_device(struct f71805f_data *data) static int f71805f_probe(struct platform_device *pdev) { - struct f71805f_sio_data *sio_data = pdev->dev.platform_data; + struct f71805f_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct f71805f_data *data; struct resource *res; int i, err; diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index cfb02dd..31b221e 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -2267,7 +2267,7 @@ static int f71882fg_create_fan_sysfs_files( static int f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; - struct f71882fg_sio_data *sio_data = pdev->dev.platform_data; + struct f71882fg_sio_data *sio_data = dev_get_platdata(&pdev->dev); int nr_fans = f71882fg_nr_fans[sio_data->type]; int nr_temps = f71882fg_nr_temps[sio_data->type]; int err, i; diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 9e300e5..a837b94 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -832,7 +832,8 @@ static int f75375_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct f75375_data *data; - struct f75375s_platform_data *f75375s_pdata = client->dev.platform_data; + struct f75375s_platform_data *f75375s_pdata = + dev_get_platdata(&client->dev); int err; if (!i2c_check_functionality(client->adapter, diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 73adf01..b4b8b5b 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -717,7 +717,7 @@ static void g762_of_clock_disable(struct i2c_client *client) { } static int g762_pdata_prop_import(struct i2c_client *client) { - struct g762_platform_data *pdata = client->dev.platform_data; + struct g762_platform_data *pdata = dev_get_platdata(&client->dev); int ret; if (!pdata) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 3104149..b7d6a57 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -495,7 +495,7 @@ static int gpio_fan_probe(struct platform_device *pdev) { int err; struct gpio_fan_data *fan_data; - struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; + struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); #ifdef CONFIG_OF_GPIO if (!pdata) { diff --git a/drivers/hwmon/htu21.c b/drivers/hwmon/htu21.c new file mode 100644 index 0000000..839086e --- /dev/null +++ b/drivers/hwmon/htu21.c @@ -0,0 +1,199 @@ +/* + * Measurement Specialties HTU21D humidity and temperature sensor driver + * + * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.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. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/jiffies.h> + +/* HTU21 Commands */ +#define HTU21_T_MEASUREMENT_HM 0xE3 +#define HTU21_RH_MEASUREMENT_HM 0xE5 + +struct htu21 { + struct device *hwmon_dev; + struct mutex lock; + bool valid; + unsigned long last_update; + int temperature; + int humidity; +}; + +static inline int htu21_temp_ticks_to_millicelsius(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((21965 * ticks) >> 13) - 46850; +} + +static inline int htu21_rh_ticks_to_per_cent_mille(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((15625 * ticks) >> 13) - 6000; +} + +static int htu21_update_measurements(struct i2c_client *client) +{ + int ret = 0; + struct htu21 *htu21 = i2c_get_clientdata(client); + + mutex_lock(&htu21->lock); + + if (time_after(jiffies, htu21->last_update + HZ / 2) || + !htu21->valid) { + ret = i2c_smbus_read_word_swapped(client, + HTU21_T_MEASUREMENT_HM); + if (ret < 0) + goto out; + htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); + ret = i2c_smbus_read_word_swapped(client, + HTU21_RH_MEASUREMENT_HM); + if (ret < 0) + goto out; + htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); + htu21->last_update = jiffies; + htu21->valid = true; + } +out: + mutex_unlock(&htu21->lock); + + return ret >= 0 ? 0 : ret; +} + +static ssize_t htu21_show_temperature(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct htu21 *htu21 = i2c_get_clientdata(client); + int ret = htu21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", htu21->temperature); +} + +static ssize_t htu21_show_humidity(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct htu21 *htu21 = i2c_get_clientdata(client); + int ret = htu21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", htu21->humidity); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + htu21_show_temperature, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, + htu21_show_humidity, NULL, 0); + +static struct attribute *htu21_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_humidity1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group htu21_group = { + .attrs = htu21_attributes, +}; + +static int htu21_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct htu21 *htu21; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + dev_err(&client->dev, + "adapter does not support SMBus word transactions\n"); + return -ENODEV; + } + + htu21 = devm_kzalloc(&client->dev, sizeof(*htu21), GFP_KERNEL); + if (!htu21) + return -ENOMEM; + + i2c_set_clientdata(client, htu21); + + mutex_init(&htu21->lock); + + err = sysfs_create_group(&client->dev.kobj, &htu21_group); + if (err) { + dev_dbg(&client->dev, "could not create sysfs files\n"); + return err; + } + htu21->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(htu21->hwmon_dev)) { + dev_dbg(&client->dev, "unable to register hwmon device\n"); + err = PTR_ERR(htu21->hwmon_dev); + goto error; + } + + dev_info(&client->dev, "initialized\n"); + + return 0; + +error: + sysfs_remove_group(&client->dev.kobj, &htu21_group); + return err; +} + +static int htu21_remove(struct i2c_client *client) +{ + struct htu21 *htu21 = i2c_get_clientdata(client); + + hwmon_device_unregister(htu21->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &htu21_group); + + return 0; +} + +static const struct i2c_device_id htu21_id[] = { + { "htu21", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, htu21_id); + +static struct i2c_driver htu21_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "htu21", + }, + .probe = htu21_probe, + .remove = htu21_remove, + .id_table = htu21_id, +}; + +module_i2c_driver(htu21_driver); + +MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); +MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index de058c2..6c0080a 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -3,7 +3,7 @@ * temperature sensors * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -609,7 +609,7 @@ static void __exit i5k_amb_exit(void) platform_driver_unregister(&i5k_amb_driver); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 1429f6e..e2b56a2 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -3,7 +3,7 @@ * temperature/power/energy sensors and capping functionality. * Copyright (C) 2008 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -1103,7 +1103,7 @@ static void __exit aem_exit(void) aem_delete(p1); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 74b365e..20ab0fb 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -2,7 +2,7 @@ * A hwmon driver for the IBM PowerExecutive temperature/power sensors * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -606,7 +606,7 @@ static void __exit ibmpex_exit(void) ibmpex_bmc_delete(p); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index d917a2d..18c0623 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -232,9 +232,9 @@ static int ina2xx_probe(struct i2c_client *client, if (!data) return -ENOMEM; - if (client->dev.platform_data) { + if (dev_get_platdata(&client->dev)) { pdata = - (struct ina2xx_platform_data *)client->dev.platform_data; + (struct ina2xx_platform_data *)dev_get_platdata(&client->dev); shunt = pdata->shunt_uohms; } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", &val)) { diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 72b21d5..29ffa27 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1962,7 +1962,7 @@ exit: static void it87_remove_files(struct device *dev) { struct it87_data *data = platform_get_drvdata(pdev); - struct it87_sio_data *sio_data = dev->platform_data; + struct it87_sio_data *sio_data = dev_get_platdata(dev); int i; sysfs_remove_group(&dev->kobj, &it87_group); @@ -2014,7 +2014,7 @@ static int it87_probe(struct platform_device *pdev) struct it87_data *data; struct resource *res; struct device *dev = &pdev->dev; - struct it87_sio_data *sio_data = dev->platform_data; + struct it87_sio_data *sio_data = dev_get_platdata(dev); int err = 0, i; int enable_pwm_interface; int fan_beep_need_rw; @@ -2316,7 +2316,7 @@ static int it87_check_pwm(struct device *dev) /* Called when we have found a new IT87. */ static void it87_init_device(struct platform_device *pdev) { - struct it87_sio_data *sio_data = pdev->dev.platform_data; + struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct it87_data *data = platform_get_drvdata(pdev); int tmp, i; u8 mask; diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index e3b037c..e633856 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,5 +1,5 @@ /* - * k10temp.c - AMD Family 10h/11h/12h/14h/15h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> * @@ -211,6 +211,7 @@ static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 16e45d7..333092c 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -855,8 +855,8 @@ static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - if (client->dev.platform_data) { - data->channel = *(u8 *)client->dev.platform_data; + if (dev_get_platdata(&client->dev)) { + data->channel = *(u8 *)dev_get_platdata(&client->dev); lm87_write_value(client, LM87_REG_CHANNEL_MODE, data->channel); } else { diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index b5ebb91..96dccaf 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -261,7 +261,7 @@ static int max197_probe(struct platform_device *pdev) { int ch, ret; struct max197_data *data; - struct max197_platform_data *pdata = pdev->dev.platform_data; + struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev); enum max197_chips chip = platform_get_device_id(pdev)->driver_data; if (pdata == NULL) { diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 3e7b426..066e587 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -428,7 +428,7 @@ static int max6639_init_client(struct i2c_client *client) { struct max6639_data *data = i2c_get_clientdata(client); struct max6639_platform_data *max6639_info = - client->dev.platform_data; + dev_get_platdata(&client->dev); int i; int rpm_range = 1; /* default: 4000 RPM */ int err; diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index eedb322..d219c06 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -143,12 +143,13 @@ static int mcp3021_probe(struct i2c_client *client, break; } - if (client->dev.platform_data) { - data->vdd = *(u32 *)client->dev.platform_data; + if (dev_get_platdata(&client->dev)) { + data->vdd = *(u32 *)dev_get_platdata(&client->dev); if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN) return -EINVAL; - } else + } else { data->vdd = MCP3021_VDD_REF; + } err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); if (err) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 99cec18..6eb03ce 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -33,9 +33,11 @@ * Supports the following chips: * * Chip #vin #fan #pwm #temp chip IDs man ID + * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3 * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 + * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -59,13 +61,15 @@ #define USE_ALTERNATE -enum kinds { nct6775, nct6776, nct6779 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { + "nct6106", "nct6775", "nct6776", "nct6779", + "nct6791", }; static unsigned short force_id; @@ -91,9 +95,11 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ +#define SIO_NCT6106_ID 0xc450 #define SIO_NCT6775_ID 0xb470 #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 +#define SIO_NCT6791_ID 0xc800 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -167,7 +173,10 @@ superio_exit(int ioreg) #define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ #define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ -#define NUM_REG_ALARM 4 /* Max number of alarm registers */ +#define NUM_REG_ALARM 7 /* Max number of alarm registers */ +#define NUM_REG_BEEP 5 /* Max number of beep registers */ + +#define NUM_FAN 6 /* Common and NCT6775 specific data */ @@ -185,6 +194,7 @@ static const u16 NCT6775_REG_IN[] = { #define NCT6775_REG_VBAT 0x5D #define NCT6775_REG_DIODE 0x5E +#define NCT6775_DIODE_MASK 0x02 #define NCT6775_REG_FANDIV1 0x506 #define NCT6775_REG_FANDIV2 0x507 @@ -193,7 +203,7 @@ static const u16 NCT6775_REG_IN[] = { static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; -/* 0..15 voltages, 16..23 fans, 24..31 temperatures */ +/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */ static const s8 NCT6775_ALARM_BITS[] = { 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ @@ -208,6 +218,23 @@ static const s8 NCT6775_ALARM_BITS[] = { #define TEMP_ALARM_BASE 24 #define INTRUSION_ALARM_BASE 30 +static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e }; + +/* + * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures, + * 30..31 intrusion + */ +static const s8 NCT6775_BEEP_BITS[] = { + 0, 1, 2, 3, 8, 9, 10, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + 21, /* global beep enable */ + 6, 7, 11, 28, -1, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, -1 }; /* intrusion0, intrusion1 */ + +#define BEEP_ENABLE_BASE 15 + static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; @@ -217,27 +244,32 @@ static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; /* Advanced Fan control, some values are common for all fans */ -static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 }; -static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 }; +static const u16 NCT6775_REG_TARGET[] = { + 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 }; +static const u16 NCT6775_REG_FAN_MODE[] = { + 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 }; static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = { - 0x103, 0x203, 0x303, 0x803, 0x903 }; + 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 }; static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = { - 0x104, 0x204, 0x304, 0x804, 0x904 }; + 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04 }; static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { - 0x105, 0x205, 0x305, 0x805, 0x905 }; -static const u16 NCT6775_REG_FAN_START_OUTPUT[] - = { 0x106, 0x206, 0x306, 0x806, 0x906 }; + 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05 }; +static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { + 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06 }; static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; static const u16 NCT6775_REG_FAN_STOP_TIME[] = { - 0x107, 0x207, 0x307, 0x807, 0x907 }; -static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 }; -static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 }; + 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07 }; +static const u16 NCT6775_REG_PWM[] = { + 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09 }; +static const u16 NCT6775_REG_PWM_READ[] = { + 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09 }; static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 }; +static const u16 NCT6775_FAN_PULSE_SHIFT[] = { 0, 0, 0, 0, 0, 0 }; static const u16 NCT6775_REG_TEMP[] = { 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; @@ -253,25 +285,25 @@ static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 }; static const u16 NCT6775_REG_TEMP_SEL[] = { - 0x100, 0x200, 0x300, 0x800, 0x900 }; + 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00 }; static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = { - 0x139, 0x239, 0x339, 0x839, 0x939 }; + 0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 }; static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = { - 0x13a, 0x23a, 0x33a, 0x83a, 0x93a }; + 0x13a, 0x23a, 0x33a, 0x83a, 0x93a, 0xa3a }; static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = { - 0x13b, 0x23b, 0x33b, 0x83b, 0x93b }; + 0x13b, 0x23b, 0x33b, 0x83b, 0x93b, 0xa3b }; static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = { - 0x13c, 0x23c, 0x33c, 0x83c, 0x93c }; + 0x13c, 0x23c, 0x33c, 0x83c, 0x93c, 0xa3c }; static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = { - 0x13d, 0x23d, 0x33d, 0x83d, 0x93d }; + 0x13d, 0x23d, 0x33d, 0x83d, 0x93d, 0xa3d }; static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; static const u16 NCT6775_REG_AUTO_TEMP[] = { - 0x121, 0x221, 0x321, 0x821, 0x921 }; + 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21 }; static const u16 NCT6775_REG_AUTO_PWM[] = { - 0x127, 0x227, 0x327, 0x827, 0x927 }; + 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27 }; #define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p)) #define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p)) @@ -279,9 +311,9 @@ static const u16 NCT6775_REG_AUTO_PWM[] = { static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 }; static const u16 NCT6775_REG_CRITICAL_TEMP[] = { - 0x135, 0x235, 0x335, 0x835, 0x935 }; + 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35 }; static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = { - 0x138, 0x238, 0x338, 0x838, 0x938 }; + 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38 }; static const char *const nct6775_temp_label[] = { "", @@ -325,17 +357,28 @@ static const s8 NCT6776_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ +static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 }; + +static const s8 NCT6776_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ + 8, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + 24, /* global beep enable */ + 25, 26, 27, 28, 29, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 30, 31 }; /* intrusion0, intrusion1 */ + static const u16 NCT6776_REG_TOLERANCE_H[] = { - 0x10c, 0x20c, 0x30c, 0x80c, 0x90c }; + 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c }; -static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 }; -static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 }; +static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 }; +static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 }; static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = { - 0x13e, 0x23e, 0x33e, 0x83e, 0x93e }; + 0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e }; static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; @@ -390,14 +433,25 @@ static const s8 NCT6779_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ -static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; +static const s8 NCT6779_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ + 8, 9, 10, 11, 12, 13, 14, /* in8..in14 */ + 24, /* global beep enable */ + 25, 26, 27, 28, 29, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, -1, -1, -1, -1, /* temp1..temp6 */ + 30, 31 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6779_REG_FAN[] = { + 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba }; static const u16 NCT6779_REG_FAN_PULSES[] = { - 0x644, 0x645, 0x646, 0x647, 0x648 }; + 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 }; static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = { - 0x136, 0x236, 0x336, 0x836, 0x936 }; + 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36 }; +#define NCT6779_CRITICAL_PWM_ENABLE_MASK 0x01 static const u16 NCT6779_REG_CRITICAL_PWM[] = { - 0x137, 0x237, 0x337, 0x837, 0x937 }; + 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 }; static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { @@ -449,6 +503,122 @@ static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; +/* NCT6791 specific data */ + +#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 + +static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568, 0x45D }; + +static const s8 NCT6791_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, 33, /* fan1..fan6 */ + -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + + +/* NCT6102D/NCT6106D specific data */ + +#define NCT6106_REG_VBAT 0x318 +#define NCT6106_REG_DIODE 0x319 +#define NCT6106_DIODE_MASK 0x01 + +static const u16 NCT6106_REG_IN_MAX[] = { + 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9e, 0xa0, 0xa2 }; +static const u16 NCT6106_REG_IN_MIN[] = { + 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9f, 0xa1, 0xa3 }; +static const u16 NCT6106_REG_IN[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09 }; + +static const u16 NCT6106_REG_TEMP[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 }; +static const u16 NCT6106_REG_TEMP_HYST[] = { + 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7 }; +static const u16 NCT6106_REG_TEMP_OVER[] = { + 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd6 }; +static const u16 NCT6106_REG_TEMP_CRIT_L[] = { + 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4 }; +static const u16 NCT6106_REG_TEMP_CRIT_H[] = { + 0xc1, 0xc5, 0xc9, 0xcf, 0xd1, 0xd5 }; +static const u16 NCT6106_REG_TEMP_OFFSET[] = { 0x311, 0x312, 0x313 }; +static const u16 NCT6106_REG_TEMP_CONFIG[] = { + 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc }; + +static const u16 NCT6106_REG_FAN[] = { 0x20, 0x22, 0x24 }; +static const u16 NCT6106_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4 }; +static const u16 NCT6106_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0, 0 }; +static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4, 0, 0 }; + +static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 }; +static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 }; +static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 }; +static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c }; +static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 }; +static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 }; +static const u16 NCT6106_REG_TEMP_SOURCE[] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 }; + +static const u16 NCT6106_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a }; +static const u16 NCT6106_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x11b, 0x12b, 0x13b }; + +static const u16 NCT6106_REG_CRITICAL_PWM_ENABLE[] = { 0x11c, 0x12c, 0x13c }; +#define NCT6106_CRITICAL_PWM_ENABLE_MASK 0x10 +static const u16 NCT6106_REG_CRITICAL_PWM[] = { 0x11d, 0x12d, 0x13d }; + +static const u16 NCT6106_REG_FAN_STEP_UP_TIME[] = { 0x114, 0x124, 0x134 }; +static const u16 NCT6106_REG_FAN_STEP_DOWN_TIME[] = { 0x115, 0x125, 0x135 }; +static const u16 NCT6106_REG_FAN_STOP_OUTPUT[] = { 0x116, 0x126, 0x136 }; +static const u16 NCT6106_REG_FAN_START_OUTPUT[] = { 0x117, 0x127, 0x137 }; +static const u16 NCT6106_REG_FAN_STOP_TIME[] = { 0x118, 0x128, 0x138 }; +static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + +static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x17c }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; + +static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; +static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; + +static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = { + 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d }; + +static const s8 NCT6106_ALARM_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 32, 33, 34, -1, -1, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 48, -1 /* intrusion0, intrusion1 */ +}; + +static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = { + 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 }; + +static const s8 NCT6106_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ + 32, /* global beep enable */ + 24, 25, 26, 27, 28, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 34, -1 /* intrusion0, intrusion1 */ +}; + +static const u16 NCT6106_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x51, 0x52, 0x54 }; + +static const u16 NCT6106_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x204, 0x205 }; + static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) { if (mode == 0 && pwm == 255) @@ -550,13 +720,18 @@ static inline u8 in_to_reg(u32 val, u8 nr) struct nct6775_data { int addr; /* IO base of hw monitor block */ + int sioreg; /* SIO register address */ enum kinds kind; const char *name; struct device *hwmon_dev; + struct attribute_group *group_in; + struct attribute_group *group_fan; + struct attribute_group *group_temp; + struct attribute_group *group_pwm; - u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, - * 3=temp_crit + u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit */ u8 temp_src[NUM_TEMP]; u16 reg_temp_config[NUM_TEMP]; @@ -566,8 +741,10 @@ struct nct6775_data { u16 REG_CONFIG; u16 REG_VBAT; u16 REG_DIODE; + u8 DIODE_MASK; const s8 *ALARM_BITS; + const s8 *BEEP_BITS; const u16 *REG_VIN; const u16 *REG_IN_MINMAX[2]; @@ -577,6 +754,7 @@ struct nct6775_data { const u16 *REG_FAN_MODE; const u16 *REG_FAN_MIN; const u16 *REG_FAN_PULSES; + const u16 *FAN_PULSE_SHIFT; const u16 *REG_FAN_TIME[3]; const u16 *REG_TOLERANCE_H; @@ -590,6 +768,10 @@ struct nct6775_data { */ const u16 *REG_PWM_READ; + const u16 *REG_CRITICAL_PWM_ENABLE; + u8 CRITICAL_PWM_ENABLE_MASK; + const u16 *REG_CRITICAL_PWM; + const u16 *REG_AUTO_TEMP; const u16 *REG_AUTO_PWM; @@ -604,6 +786,7 @@ struct nct6775_data { const u16 *REG_TEMP_OFFSET; const u16 *REG_ALARM; + const u16 *REG_BEEP; unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); @@ -616,26 +799,30 @@ struct nct6775_data { u8 bank; /* current register bank */ u8 in_num; /* number of in inputs we have */ u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ - unsigned int rpm[5]; - u16 fan_min[5]; - u8 fan_pulses[5]; - u8 fan_div[5]; + unsigned int rpm[NUM_FAN]; + u16 fan_min[NUM_FAN]; + u8 fan_pulses[NUM_FAN]; + u8 fan_div[NUM_FAN]; u8 has_pwm; u8 has_fan; /* some fan inputs can be disabled */ u8 has_fan_min; /* some fans don't have min register */ bool has_fan_div; - u8 num_temp_alarms; /* 2 or 3 */ + u8 num_temp_alarms; /* 2, 3, or 6 */ + u8 num_temp_beeps; /* 2, 3, or 6 */ u8 temp_fixed_num; /* 3 or 6 */ u8 temp_type[NUM_TEMP_FIXED]; s8 temp_offset[NUM_TEMP_FIXED]; - s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, - * 3=temp_crit */ + s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit */ u64 alarms; + u64 beeps; u8 pwm_num; /* number of pwm */ - u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */ - enum pwm_enable pwm_enable[5]; + u8 pwm_mode[NUM_FAN]; /* 1->DC variable voltage, + * 0->PWM variable duty cycle + */ + enum pwm_enable pwm_enable[NUM_FAN]; /* 0->off * 1->manual * 2->thermal cruise mode (also called SmartFan I) @@ -643,35 +830,37 @@ struct nct6775_data { * 4->SmartFan III * 5->enhanced variable thermal cruise (SmartFan IV) */ - u8 pwm[7][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, - * [3]=pwm_max, [4]=pwm_step, - * [5]=weight_duty_step, [6]=weight_duty_base - */ + u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ - u8 target_temp[5]; + u8 target_temp[NUM_FAN]; u8 target_temp_mask; - u32 target_speed[5]; - u32 target_speed_tolerance[5]; + u32 target_speed[NUM_FAN]; + u32 target_speed_tolerance[NUM_FAN]; u8 speed_tolerance_limit; - u8 temp_tolerance[2][5]; + u8 temp_tolerance[2][NUM_FAN]; u8 tolerance_mask; - u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ + u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ /* Automatic fan speed control registers */ int auto_pwm_num; - u8 auto_pwm[5][7]; - u8 auto_temp[5][7]; - u8 pwm_temp_sel[5]; - u8 pwm_weight_temp_sel[5]; - u8 weight_temp[3][5]; /* 0->temp_step, 1->temp_step_tol, - * 2->temp_base - */ + u8 auto_pwm[NUM_FAN][7]; + u8 auto_temp[NUM_FAN][7]; + u8 pwm_temp_sel[NUM_FAN]; + u8 pwm_weight_temp_sel[NUM_FAN]; + u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol, + * 2->temp_base + */ u8 vid; u8 vrm; + bool have_vid; + u16 have_temp; u16 have_temp_fixed; u16 have_in; @@ -688,9 +877,145 @@ struct nct6775_sio_data { enum kinds kind; }; +struct sensor_device_template { + struct device_attribute dev_attr; + union { + struct { + u8 nr; + u8 index; + } s; + int index; + } u; + bool s2; /* true if both index and nr are used */ +}; + +struct sensor_device_attr_u { + union { + struct sensor_device_attribute a1; + struct sensor_device_attribute_2 a2; + } u; + char name[32]; +}; + +#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \ + .attr = {.name = _template, .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.index = _index, \ + .s2 = false } + +#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.s.index = _index, \ + .u.s.nr = _nr, \ + .s2 = true } + +#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \ + _index) + +#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \ + _nr, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) + +struct sensor_template_group { + struct sensor_device_template **templates; + umode_t (*is_visible)(struct kobject *, struct attribute *, int); + int base; +}; + +static struct attribute_group * +nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, + int repeat) +{ + struct attribute_group *group; + struct sensor_device_attr_u *su; + struct sensor_device_attribute *a; + struct sensor_device_attribute_2 *a2; + struct attribute **attrs; + struct sensor_device_template **t; + int err, i, j, count; + + if (repeat <= 0) + return ERR_PTR(-EINVAL); + + t = tg->templates; + for (count = 0; *t; t++, count++) + ; + + if (count == 0) + return ERR_PTR(-EINVAL); + + group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); + if (group == NULL) + return ERR_PTR(-ENOMEM); + + attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1), + GFP_KERNEL); + if (attrs == NULL) + return ERR_PTR(-ENOMEM); + + su = devm_kzalloc(dev, sizeof(*su) * repeat * count, + GFP_KERNEL); + if (su == NULL) + return ERR_PTR(-ENOMEM); + + group->attrs = attrs; + group->is_visible = tg->is_visible; + + for (i = 0; i < repeat; i++) { + t = tg->templates; + for (j = 0; *t != NULL; j++) { + snprintf(su->name, sizeof(su->name), + (*t)->dev_attr.attr.name, tg->base + i); + if ((*t)->s2) { + a2 = &su->u.a2; + a2->dev_attr.attr.name = su->name; + a2->nr = (*t)->u.s.nr + i; + a2->index = (*t)->u.s.index; + a2->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a2->dev_attr.show = (*t)->dev_attr.show; + a2->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a2->dev_attr.attr; + } else { + a = &su->u.a1; + a->dev_attr.attr.name = su->name; + a->index = (*t)->u.index + i; + a->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a->dev_attr.show = (*t)->dev_attr.show; + a->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a->dev_attr.attr; + } + attrs++; + su++; + t++; + } + } + + err = sysfs_create_group(&dev->kobj, group); + if (err) + return ERR_PTR(-ENOMEM); + + return group; +} + static bool is_word_sized(struct nct6775_data *data, u16 reg) { switch (data->kind) { + case nct6106: + return reg == 0x20 || reg == 0x22 || reg == 0x24 || + reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || + reg == 0x111 || reg == 0x121 || reg == 0x131; case nct6775: return (((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) && @@ -714,8 +1039,9 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6779: + case nct6791: return reg == 0x150 || reg == 0x153 || reg == 0x155 || - ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) || + ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || @@ -1056,15 +1382,17 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6776: data->auto_pwm[i][data->auto_pwm_num] = 0xff; break; + case nct6106: case nct6779: + case nct6791: reg = nct6775_read_value(data, - NCT6779_REG_CRITICAL_PWM_ENABLE[i]); - if (reg & 1) - data->auto_pwm[i][data->auto_pwm_num] = - nct6775_read_value(data, - NCT6779_REG_CRITICAL_PWM[i]); + data->REG_CRITICAL_PWM_ENABLE[i]); + if (reg & data->CRITICAL_PWM_ENABLE_MASK) + reg = nct6775_read_value(data, + data->REG_CRITICAL_PWM[i]); else - data->auto_pwm[i][data->auto_pwm_num] = 0xff; + reg = 0xff; + data->auto_pwm[i][data->auto_pwm_num] = reg; break; } } @@ -1110,7 +1438,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->fan_min[i] = nct6775_read_value(data, data->REG_FAN_MIN[i]); data->fan_pulses[i] = - nct6775_read_value(data, data->REG_FAN_PULSES[i]); + (nct6775_read_value(data, data->REG_FAN_PULSES[i]) + >> data->FAN_PULSE_SHIFT[i]) & 0x03; nct6775_select_fan_div(dev, data, i, reg); } @@ -1143,6 +1472,15 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->alarms |= ((u64)alarm) << (i << 3); } + data->beeps = 0; + for (i = 0; i < NUM_REG_BEEP; i++) { + u8 beep; + if (!data->REG_BEEP[i]) + continue; + beep = nct6775_read_value(data, data->REG_BEEP[i]); + data->beeps |= ((u64)beep) << (i << 3); + } + data->last_updated = jiffies; data->valid = true; } @@ -1230,224 +1568,138 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%u\n", alarm); } -static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0); -static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0); -static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0); -static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0); -static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0); -static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0); -static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0); -static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0); -static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0); -static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0); -static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0); -static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0); -static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0); -static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0); -static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0); - -static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); -static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); -static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4); -static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); -static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); -static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); -static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); -static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9); -static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10); -static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11); -static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12); -static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13); -static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14); - -static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 0, 1); -static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 1, 1); -static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 2, 1); -static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 3, 1); -static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 4, 1); -static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 5, 1); -static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 6, 1); -static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 7, 1); -static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 8, 1); -static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 9, 1); -static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 10, 1); -static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 11, 1); -static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 12, 1); -static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 13, 1); -static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 14, 1); - -static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 0, 2); -static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 1, 2); -static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 2, 2); -static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 3, 2); -static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 4, 2); -static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 5, 2); -static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 6, 2); -static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 7, 2); -static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 8, 2); -static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 9, 2); -static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 10, 2); -static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 11, 2); -static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 12, 2); -static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 13, 2); -static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 14, 2); - -static struct attribute *nct6775_attributes_in[15][5] = { - { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in0_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in1_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in2_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in3_max.dev_attr.attr, - &sensor_dev_attr_in3_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in4_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in5_min.dev_attr.attr, - &sensor_dev_attr_in5_max.dev_attr.attr, - &sensor_dev_attr_in5_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in6_min.dev_attr.attr, - &sensor_dev_attr_in6_max.dev_attr.attr, - &sensor_dev_attr_in6_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in7_min.dev_attr.attr, - &sensor_dev_attr_in7_max.dev_attr.attr, - &sensor_dev_attr_in7_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in8_input.dev_attr.attr, - &sensor_dev_attr_in8_min.dev_attr.attr, - &sensor_dev_attr_in8_max.dev_attr.attr, - &sensor_dev_attr_in8_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in9_input.dev_attr.attr, - &sensor_dev_attr_in9_min.dev_attr.attr, - &sensor_dev_attr_in9_max.dev_attr.attr, - &sensor_dev_attr_in9_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in10_min.dev_attr.attr, - &sensor_dev_attr_in10_max.dev_attr.attr, - &sensor_dev_attr_in10_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in11_input.dev_attr.attr, - &sensor_dev_attr_in11_min.dev_attr.attr, - &sensor_dev_attr_in11_max.dev_attr.attr, - &sensor_dev_attr_in11_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in12_input.dev_attr.attr, - &sensor_dev_attr_in12_min.dev_attr.attr, - &sensor_dev_attr_in12_max.dev_attr.attr, - &sensor_dev_attr_in12_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in13_input.dev_attr.attr, - &sensor_dev_attr_in13_min.dev_attr.attr, - &sensor_dev_attr_in13_max.dev_attr.attr, - &sensor_dev_attr_in13_alarm.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_in14_input.dev_attr.attr, - &sensor_dev_attr_in14_min.dev_attr.attr, - &sensor_dev_attr_in14_max.dev_attr.attr, - &sensor_dev_attr_in14_alarm.dev_attr.attr, - NULL - }, +static ssize_t +show_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6775_data *data = nct6775_update_device(dev); + int nr = data->BEEP_BITS[sattr->index]; + + return sprintf(buf, "%u\n", + (unsigned int)((data->beeps >> nr) & 0x01)); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6775_data *data = dev_get_drvdata(dev); + int nr = data->BEEP_BITS[sattr->index]; + int regindex = nr >> 3; + unsigned long val; + + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val) + data->beeps |= (1ULL << nr); + else + data->beeps &= ~(1ULL << nr); + nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6775_data *data = nct6775_update_device(dev); + unsigned int beep = 0; + int nr; + + /* + * For temperatures, there is no fixed mapping from registers to beep + * enable bits. Beep enable bits are determined by the temperature + * source mapping. + */ + nr = find_temp_source(data, sattr->index, data->num_temp_beeps); + if (nr >= 0) { + int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; + } + return sprintf(buf, "%u\n", beep); +} + +static ssize_t +store_temp_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6775_data *data = dev_get_drvdata(dev); + int nr, bit, regindex; + unsigned long val; + + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + nr = find_temp_source(data, sattr->index, data->num_temp_beeps); + if (nr < 0) + return -ENODEV; + + bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + regindex = bit >> 3; + + mutex_lock(&data->update_lock); + if (val) + data->beeps |= (1ULL << bit); + else + data->beeps &= ~(1ULL << bit); + nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); + mutex_unlock(&data->update_lock); + + return count; +} + +static umode_t nct6775_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int in = index / 5; /* voltage index */ + + if (!(data->have_in & (1 << in))) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0); +SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0); +SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep, + 0); +SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 1); +SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 2); + +/* + * nct6775_in_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_in_template[] = { + &sensor_dev_template_in_input, + &sensor_dev_template_in_alarm, + &sensor_dev_template_in_beep, + &sensor_dev_template_in_min, + &sensor_dev_template_in_max, + NULL }; -static const struct attribute_group nct6775_group_in[15] = { - { .attrs = nct6775_attributes_in[0] }, - { .attrs = nct6775_attributes_in[1] }, - { .attrs = nct6775_attributes_in[2] }, - { .attrs = nct6775_attributes_in[3] }, - { .attrs = nct6775_attributes_in[4] }, - { .attrs = nct6775_attributes_in[5] }, - { .attrs = nct6775_attributes_in[6] }, - { .attrs = nct6775_attributes_in[7] }, - { .attrs = nct6775_attributes_in[8] }, - { .attrs = nct6775_attributes_in[9] }, - { .attrs = nct6775_attributes_in[10] }, - { .attrs = nct6775_attributes_in[11] }, - { .attrs = nct6775_attributes_in[12] }, - { .attrs = nct6775_attributes_in[13] }, - { .attrs = nct6775_attributes_in[14] }, +static struct sensor_template_group nct6775_in_template_group = { + .templates = nct6775_attributes_in_template, + .is_visible = nct6775_in_is_visible, }; static ssize_t @@ -1592,6 +1844,7 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr, int nr = sattr->index; unsigned long val; int err; + u8 reg; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1602,60 +1855,68 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->fan_pulses[nr] = val & 3; - nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3); + reg = nct6775_read_value(data, data->REG_FAN_PULSES[nr]); + reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]); + reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr]; + nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg); mutex_unlock(&data->update_lock); return count; } -static struct sensor_device_attribute sda_fan_input[] = { - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), - SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), -}; +static umode_t nct6775_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int fan = index / 6; /* fan index */ + int nr = index % 6; /* attribute index */ -static struct sensor_device_attribute sda_fan_alarm[] = { - SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE), - SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1), - SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2), - SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3), - SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4), -}; + if (!(data->has_fan & (1 << fan))) + return 0; -static struct sensor_device_attribute sda_fan_min[] = { - SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 0), - SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 1), - SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 2), - SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 3), - SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 4), -}; + if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1) + return 0; + if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1) + return 0; + if (nr == 4 && !(data->has_fan_min & (1 << fan))) + return 0; + if (nr == 5 && data->kind != nct6775) + return 0; + + return attr->mode; +} -static struct sensor_device_attribute sda_fan_pulses[] = { - SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 0), - SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 1), - SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 2), - SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 3), - SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 4), +SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0); +SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL, + FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep, + store_beep, FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 0); +SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 0); +SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0); + +/* + * nct6775_fan_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_fan_template[] = { + &sensor_dev_template_fan_input, + &sensor_dev_template_fan_alarm, /* 1 */ + &sensor_dev_template_fan_beep, /* 2 */ + &sensor_dev_template_fan_pulses, + &sensor_dev_template_fan_min, /* 4 */ + &sensor_dev_template_fan_div, /* 5 */ + NULL }; -static struct sensor_device_attribute sda_fan_div[] = { - SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), - SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), - SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), - SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3), - SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), +static struct sensor_template_group nct6775_fan_template_group = { + .templates = nct6775_attributes_fan_template, + .is_visible = nct6775_fan_is_visible, + .base = 1, }; static ssize_t @@ -1752,7 +2013,7 @@ store_temp_type(struct device *dev, struct device_attribute *attr, int nr = sattr->index; unsigned long val; int err; - u8 vbat, diode, bit; + u8 vbat, diode, vbit, dbit; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1764,16 +2025,17 @@ store_temp_type(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->temp_type[nr] = val; - vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr); - diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr); - bit = 0x02 << nr; + vbit = 0x02 << nr; + dbit = data->DIODE_MASK << nr; + vbat = nct6775_read_value(data, data->REG_VBAT) & ~vbit; + diode = nct6775_read_value(data, data->REG_DIODE) & ~dbit; switch (val) { case 1: /* CPU diode (diode, current mode) */ - vbat |= bit; - diode |= bit; + vbat |= vbit; + diode |= dbit; break; case 3: /* diode, voltage mode */ - vbat |= bit; + vbat |= dbit; break; case 4: /* thermistor */ break; @@ -1785,142 +2047,83 @@ store_temp_type(struct device *dev, struct device_attribute *attr, return count; } -static struct sensor_device_attribute_2 sda_temp_input[] = { - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0), - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0), - SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0), - SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0), - SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0), - SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0), - SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0), - SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0), - SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0), -}; +static umode_t nct6775_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int temp = index / 10; /* temp index */ + int nr = index % 10; /* attribute index */ -static struct sensor_device_attribute sda_temp_label[] = { - SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0), - SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1), - SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2), - SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3), - SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), - SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), - SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), - SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), - SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), - SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9), -}; + if (!(data->have_temp & (1 << temp))) + return 0; -static struct sensor_device_attribute_2 sda_temp_max[] = { - SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 0, 1), - SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 1, 1), - SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 2, 1), - SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 3, 1), - SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 4, 1), - SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 5, 1), - SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 6, 1), - SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 7, 1), - SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 8, 1), - SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp, - 9, 1), -}; + if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0) + return 0; /* alarm */ -static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { - SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 0, 2), - SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 1, 2), - SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 2, 2), - SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 3, 2), - SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 4, 2), - SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 5, 2), - SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 6, 2), - SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 7, 2), - SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 8, 2), - SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, - 9, 2), -}; + if (nr == 3 && find_temp_source(data, temp, data->num_temp_beeps) < 0) + return 0; /* beep */ -static struct sensor_device_attribute_2 sda_temp_crit[] = { - SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 0, 3), - SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 1, 3), - SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 2, 3), - SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 3, 3), - SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 4, 3), - SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 5, 3), - SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 6, 3), - SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 7, 3), - SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 8, 3), - SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, - 9, 3), -}; + if (nr == 4 && !data->reg_temp[1][temp]) /* max */ + return 0; -static struct sensor_device_attribute sda_temp_offset[] = { - SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 0), - SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 1), - SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 2), - SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 3), - SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 4), - SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 5), -}; + if (nr == 5 && !data->reg_temp[2][temp]) /* max_hyst */ + return 0; + + if (nr == 6 && !data->reg_temp[3][temp]) /* crit */ + return 0; + + if (nr == 7 && !data->reg_temp[4][temp]) /* lcrit */ + return 0; + + /* offset and type only apply to fixed sensors */ + if (nr > 7 && !(data->have_temp_fixed & (1 << temp))) + return 0; -static struct sensor_device_attribute sda_temp_type[] = { - SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 0), - SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 1), - SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 2), - SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 3), - SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 4), - SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 5), + return attr->mode; +} + +SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0); +SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0); +SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 1); +SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR, + show_temp, store_temp, 0, 2); +SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 3); +SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 4); +SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR, + show_temp_offset, store_temp_offset, 0); +SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 0); +SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0); +SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep, + store_temp_beep, 0); + +/* + * nct6775_temp_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_temp_template[] = { + &sensor_dev_template_temp_input, + &sensor_dev_template_temp_label, + &sensor_dev_template_temp_alarm, /* 2 */ + &sensor_dev_template_temp_beep, /* 3 */ + &sensor_dev_template_temp_max, /* 4 */ + &sensor_dev_template_temp_max_hyst, /* 5 */ + &sensor_dev_template_temp_crit, /* 6 */ + &sensor_dev_template_temp_lcrit, /* 7 */ + &sensor_dev_template_temp_offset, /* 8 */ + &sensor_dev_template_temp_type, /* 9 */ + NULL }; -static struct sensor_device_attribute sda_temp_alarm[] = { - SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0), - SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1), - SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2), - SENSOR_ATTR(temp4_alarm, S_IRUGO, show_temp_alarm, NULL, 3), - SENSOR_ATTR(temp5_alarm, S_IRUGO, show_temp_alarm, NULL, 4), - SENSOR_ATTR(temp6_alarm, S_IRUGO, show_temp_alarm, NULL, 5), - SENSOR_ATTR(temp7_alarm, S_IRUGO, show_temp_alarm, NULL, 6), - SENSOR_ATTR(temp8_alarm, S_IRUGO, show_temp_alarm, NULL, 7), - SENSOR_ATTR(temp9_alarm, S_IRUGO, show_temp_alarm, NULL, 8), - SENSOR_ATTR(temp10_alarm, S_IRUGO, show_temp_alarm, NULL, 9), +static struct sensor_template_group nct6775_temp_template_group = { + .templates = nct6775_attributes_temp_template, + .is_visible = nct6775_temp_is_visible, + .base = 1, }; static ssize_t @@ -2422,77 +2625,19 @@ store_speed_tolerance(struct device *dev, struct device_attribute *attr, return count; } -static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); -static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0); -static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0); -static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0); -static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0); - -static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 0); -static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 1); -static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 2); -static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 3); -static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 4); - -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 0); -static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 1); -static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 2); -static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 3); -static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 4); - -static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 0); -static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 1); -static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 2); -static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 3); -static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 4); - -static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 0); -static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 1); -static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 2); -static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 3); -static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 4); - -static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed, - store_target_speed, 0); -static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed, - store_target_speed, 1); -static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed, - store_target_speed, 2); -static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed, - store_target_speed, 3); -static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed, - store_target_speed, 4); - -static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 0); -static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 1); -static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 2); -static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 3); -static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 4); +SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); +SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 0); +SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 0); +SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 0); +SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO, + show_target_temp, store_target_temp, 0); +SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO, + show_target_speed, store_target_speed, 0); +SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 0); /* Smart Fan registers */ @@ -2531,79 +2676,18 @@ store_weight_temp(struct device *dev, struct device_attribute *attr, return count; } -static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, - 0); -static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, - 1); -static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, - 2); -static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, - 3); -static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, - 4); - -static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 0, 0); -static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 1, 0); -static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 2, 0); -static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 3, 0); -static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 4, 0); - -static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 0, 1); -static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 1, 1); -static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 2, 1); -static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 3, 1); -static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 4, 1); - -static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 0, 2); -static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 1, 2); -static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 2, 2); -static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 3, 2); -static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO, - show_weight_temp, store_weight_temp, 4, 2); - -static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 0, 5); -static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 1, 5); -static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 2, 5); -static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 3, 5); -static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 4, 5); - -/* duty_base is not supported on all chips */ -static struct sensor_device_attribute_2 sda_weight_duty_base[] = { - SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 0, 6), - SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 1, 6), - SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 2, 6), - SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 3, 6), - SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO, - show_pwm, store_pwm, 4, 6), -}; +SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0); +SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0); +SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1); +SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2); +SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", + S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5); +SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", + S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6); static ssize_t show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) @@ -2651,227 +2735,6 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 0, 0); -static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 1, 0); -static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 2, 0); -static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 3, 0); -static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 4, 0); - -static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 0, 1); -static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 1, 1); -static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 2, 1); -static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 3, 1); -static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, - store_fan_time, 4, 1); - -static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 0, 2); -static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 1, 2); -static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 2, 2); -static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 3, 2); -static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 4, 2); - -static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 0, 1); -static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 1, 1); -static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 2, 1); -static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 3, 1); -static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 4, 1); - -static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 0, 2); -static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 1, 2); -static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 2, 2); -static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 3, 2); -static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 4, 2); - -static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 0, 0); -static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 1, 0); -static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 2, 0); -static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 3, 0); -static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 4, 0); - -static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 0, 1); -static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 1, 1); -static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 2, 1); -static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 3, 1); -static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO, - show_temp_tolerance, store_temp_tolerance, 4, 1); - -/* pwm_max is not supported on all chips */ -static struct sensor_device_attribute_2 sda_pwm_max[] = { - SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 0, 3), - SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 1, 3), - SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 2, 3), - SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 3, 3), - SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 4, 3), -}; - -/* pwm_step is not supported on all chips */ -static struct sensor_device_attribute_2 sda_pwm_step[] = { - SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4), - SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4), - SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4), - SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4), - SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4), -}; - -static struct attribute *nct6775_attributes_pwm[5][20] = { - { - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_mode.dev_attr.attr, - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm1_target_temp.dev_attr.attr, - &sensor_dev_attr_fan1_target.dev_attr.attr, - &sensor_dev_attr_fan1_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm1_stop_time.dev_attr.attr, - &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr, - &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm1_start.dev_attr.attr, - &sensor_dev_attr_pwm1_floor.dev_attr.attr, - &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr, - &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr, - &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr, - &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_mode.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm2_target_temp.dev_attr.attr, - &sensor_dev_attr_fan2_target.dev_attr.attr, - &sensor_dev_attr_fan2_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm2_stop_time.dev_attr.attr, - &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr, - &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm2_start.dev_attr.attr, - &sensor_dev_attr_pwm2_floor.dev_attr.attr, - &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr, - &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr, - &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr, - &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm3_mode.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm3_target_temp.dev_attr.attr, - &sensor_dev_attr_fan3_target.dev_attr.attr, - &sensor_dev_attr_fan3_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm3_stop_time.dev_attr.attr, - &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr, - &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm3_start.dev_attr.attr, - &sensor_dev_attr_pwm3_floor.dev_attr.attr, - &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr, - &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr, - &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr, - &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm4.dev_attr.attr, - &sensor_dev_attr_pwm4_mode.dev_attr.attr, - &sensor_dev_attr_pwm4_enable.dev_attr.attr, - &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm4_target_temp.dev_attr.attr, - &sensor_dev_attr_fan4_target.dev_attr.attr, - &sensor_dev_attr_fan4_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm4_stop_time.dev_attr.attr, - &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr, - &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm4_start.dev_attr.attr, - &sensor_dev_attr_pwm4_floor.dev_attr.attr, - &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr, - &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr, - &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr, - &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm5.dev_attr.attr, - &sensor_dev_attr_pwm5_mode.dev_attr.attr, - &sensor_dev_attr_pwm5_enable.dev_attr.attr, - &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm5_target_temp.dev_attr.attr, - &sensor_dev_attr_fan5_target.dev_attr.attr, - &sensor_dev_attr_fan5_tolerance.dev_attr.attr, - &sensor_dev_attr_pwm5_stop_time.dev_attr.attr, - &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr, - &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm5_start.dev_attr.attr, - &sensor_dev_attr_pwm5_floor.dev_attr.attr, - &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr, - &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr, - &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr, - &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr, - &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr, - NULL - }, -}; - -static const struct attribute_group nct6775_group_pwm[5] = { - { .attrs = nct6775_attributes_pwm[0] }, - { .attrs = nct6775_attributes_pwm[1] }, - { .attrs = nct6775_attributes_pwm[2] }, - { .attrs = nct6775_attributes_pwm[3] }, - { .attrs = nct6775_attributes_pwm[4] }, -}; - static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2927,17 +2790,19 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, break; case nct6776: break; /* always enabled, nothing to do */ + case nct6106: case nct6779: - nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr], + case nct6791: + nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, - NCT6779_REG_CRITICAL_PWM_ENABLE[nr]); + data->REG_CRITICAL_PWM_ENABLE[nr]); if (val == 255) - reg &= ~0x01; + reg &= ~data->CRITICAL_PWM_ENABLE_MASK; else - reg |= 0x01; + reg |= data->CRITICAL_PWM_ENABLE_MASK; nct6775_write_value(data, - NCT6779_REG_CRITICAL_PWM_ENABLE[nr], + data->REG_CRITICAL_PWM_ENABLE[nr], reg); break; } @@ -2992,155 +2857,140 @@ store_auto_temp(struct device *dev, struct device_attribute *attr, return count; } +static umode_t nct6775_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int pwm = index / 36; /* pwm index */ + int nr = index % 36; /* attribute index */ + + if (!(data->has_pwm & (1 << pwm))) + return 0; + + if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */ + return 0; + if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */ + return 0; + if (nr == 21 && data->REG_PWM[6] == NULL) /* weight_duty_base */ + return 0; + + if (nr >= 22 && nr <= 35) { /* auto point */ + int api = (nr - 22) / 2; /* auto point index */ + + if (api > data->auto_pwm_num) + return 0; + } + return attr->mode; +} + +SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 0); +SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 1); +SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 2); +SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 1); +SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 2); +SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0, 0); +SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance", + S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance, + 0, 1); + +SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 0, 3); + +SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 4); + +SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0); +SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0); + +SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1); +SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1); + +SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2); +SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2); + +SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3); +SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3); + +SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4); +SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4); + +SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5); +SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5); + +SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6); +SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6); + /* - * The number of auto-point trip points is chip dependent. - * Need to check support while generating/removing attribute files. + * nct6775_pwm_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. */ -static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = { - SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 1), - SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 1), - SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 2), - SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 2), - SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 3), - SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 3), - SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 4), - SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 4), - SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 5), - SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 5), - SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 0, 6), - SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 0, 6), - - SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 0), - SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 0), - SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 1), - SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 2), - SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 2), - SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 3), - SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 3), - SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 4), - SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 4), - SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 5), - SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 5), - SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 1, 6), - SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 1, 6), - - SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 0), - SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 0), - SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 1), - SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 1), - SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 2), - SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 2), - SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 3), - SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 3), - SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 4), - SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 4), - SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 5), - SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 5), - SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 2, 6), - SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 2, 6), - - SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 0), - SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 0), - SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 1), - SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 1), - SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 2), - SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 2), - SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 3), - SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 3), - SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 4), - SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 4), - SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 5), - SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 5), - SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 3, 6), - SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 3, 6), - - SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 0), - SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 0), - SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 1), - SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 1), - SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 2), - SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 2), - SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 3), - SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 3), - SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 4), - SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 4), - SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 5), - SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 5), - SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO, - show_auto_pwm, store_auto_pwm, 4, 6), - SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO, - show_auto_temp, store_auto_temp, 4, 6), +static struct sensor_device_template *nct6775_attributes_pwm_template[] = { + &sensor_dev_template_pwm, + &sensor_dev_template_pwm_mode, + &sensor_dev_template_pwm_enable, + &sensor_dev_template_pwm_temp_sel, + &sensor_dev_template_pwm_temp_tolerance, + &sensor_dev_template_pwm_crit_temp_tolerance, + &sensor_dev_template_pwm_target_temp, + &sensor_dev_template_fan_target, + &sensor_dev_template_fan_tolerance, + &sensor_dev_template_pwm_stop_time, + &sensor_dev_template_pwm_step_up_time, + &sensor_dev_template_pwm_step_down_time, + &sensor_dev_template_pwm_start, + &sensor_dev_template_pwm_floor, + &sensor_dev_template_pwm_weight_temp_sel, + &sensor_dev_template_pwm_weight_temp_step, + &sensor_dev_template_pwm_weight_temp_step_tol, + &sensor_dev_template_pwm_weight_temp_step_base, + &sensor_dev_template_pwm_weight_duty_step, + &sensor_dev_template_pwm_max, /* 19 */ + &sensor_dev_template_pwm_step, /* 20 */ + &sensor_dev_template_pwm_weight_duty_base, /* 21 */ + &sensor_dev_template_pwm_auto_point1_pwm, /* 22 */ + &sensor_dev_template_pwm_auto_point1_temp, + &sensor_dev_template_pwm_auto_point2_pwm, + &sensor_dev_template_pwm_auto_point2_temp, + &sensor_dev_template_pwm_auto_point3_pwm, + &sensor_dev_template_pwm_auto_point3_temp, + &sensor_dev_template_pwm_auto_point4_pwm, + &sensor_dev_template_pwm_auto_point4_temp, + &sensor_dev_template_pwm_auto_point5_pwm, + &sensor_dev_template_pwm_auto_point5_temp, + &sensor_dev_template_pwm_auto_point6_pwm, + &sensor_dev_template_pwm_auto_point6_temp, + &sensor_dev_template_pwm_auto_point7_pwm, + &sensor_dev_template_pwm_auto_point7_temp, /* 35 */ + + NULL +}; + +static struct sensor_template_group nct6775_pwm_template_group = { + .templates = nct6775_attributes_pwm_template, + .is_visible = nct6775_pwm_is_visible, + .base = 1, }; static ssize_t @@ -3159,7 +3009,6 @@ clear_caseopen(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct nct6775_data *data = dev_get_drvdata(dev); - struct nct6775_sio_data *sio_data = dev->platform_data; int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; unsigned long val; u8 reg; @@ -3175,19 +3024,19 @@ clear_caseopen(struct device *dev, struct device_attribute *attr, * The CR registers are the same for all chips, and not all chips * support clearing the caseopen status through "regular" registers. */ - ret = superio_enter(sio_data->sioreg); + ret = superio_enter(data->sioreg); if (ret) { count = ret; goto error; } - superio_select(sio_data->sioreg, NCT6775_LD_ACPI); - reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); + superio_select(data->sioreg, NCT6775_LD_ACPI); + reg = superio_inb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; - superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; - superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); - superio_exit(sio_data->sioreg); + superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + superio_exit(data->sioreg); data->valid = false; /* Force cache refresh */ error: @@ -3195,71 +3044,79 @@ error: return count; } -static struct sensor_device_attribute sda_caseopen[] = { - SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, - clear_caseopen, INTRUSION_ALARM_BASE), - SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, - clear_caseopen, INTRUSION_ALARM_BASE + 1), -}; - -/* - * Driver and device management - */ - -static void nct6775_device_remove_files(struct device *dev) +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep, + store_beep, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep, + store_beep, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep, + store_beep, BEEP_ENABLE_BASE); + +static umode_t nct6775_other_is_visible(struct kobject *kobj, + struct attribute *attr, int index) { - /* - * some entries in the following arrays may not have been used in - * device_create_file(), but device_remove_file() will ignore them - */ - int i; + struct device *dev = container_of(kobj, struct device, kobj); struct nct6775_data *data = dev_get_drvdata(dev); - for (i = 0; i < data->pwm_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]); + if (index == 1 && !data->have_vid) + return 0; - for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++) - device_remove_file(dev, &sda_pwm_max[i].dev_attr); + if (index == 2 || index == 3) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 2] < 0) + return 0; + } + + if (index == 4 || index == 5) { + if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 4] < 0) + return 0; + } - for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++) - device_remove_file(dev, &sda_pwm_step[i].dev_attr); + return attr->mode; +} - for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++) - device_remove_file(dev, &sda_weight_duty_base[i].dev_attr); +/* + * nct6775_other_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct attribute *nct6775_attributes_other[] = { + &dev_attr_name.attr, + &dev_attr_cpu0_vid.attr, /* 1 */ + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 2 */ + &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 3 */ + &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 5 */ + &sensor_dev_attr_beep_enable.dev_attr.attr, /* 6 */ + + NULL +}; - for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) - device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr); +static const struct attribute_group nct6775_group_other = { + .attrs = nct6775_attributes_other, + .is_visible = nct6775_other_is_visible, +}; - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); +/* + * Driver and device management + */ - for (i = 0; i < 5; i++) { - device_remove_file(dev, &sda_fan_input[i].dev_attr); - device_remove_file(dev, &sda_fan_alarm[i].dev_attr); - device_remove_file(dev, &sda_fan_div[i].dev_attr); - device_remove_file(dev, &sda_fan_min[i].dev_attr); - device_remove_file(dev, &sda_fan_pulses[i].dev_attr); - } - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; - device_remove_file(dev, &sda_temp_input[i].dev_attr); - device_remove_file(dev, &sda_temp_label[i].dev_attr); - device_remove_file(dev, &sda_temp_max[i].dev_attr); - device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); - device_remove_file(dev, &sda_temp_crit[i].dev_attr); - device_remove_file(dev, &sda_temp_alarm[i].dev_attr); - if (!(data->have_temp_fixed & (1 << i))) - continue; - device_remove_file(dev, &sda_temp_type[i].dev_attr); - device_remove_file(dev, &sda_temp_offset[i].dev_attr); - } +static void nct6775_device_remove_files(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); - device_remove_file(dev, &sda_caseopen[0].dev_attr); - device_remove_file(dev, &sda_caseopen[1].dev_attr); + if (data->group_pwm) + sysfs_remove_group(&dev->kobj, data->group_pwm); + if (data->group_in) + sysfs_remove_group(&dev->kobj, data->group_in); + if (data->group_fan) + sysfs_remove_group(&dev->kobj, data->group_fan); + if (data->group_temp) + sysfs_remove_group(&dev->kobj, data->group_temp); - device_remove_file(dev, &dev_attr_name); - device_remove_file(dev, &dev_attr_cpu0_vid); + sysfs_remove_group(&dev->kobj, &nct6775_group_other); } /* Get the monitoring functions started */ @@ -3297,68 +3154,78 @@ static inline void nct6775_init_device(struct nct6775_data *data) for (i = 0; i < data->temp_fixed_num; i++) { if (!(data->have_temp_fixed & (1 << i))) continue; - if ((tmp & (0x02 << i))) /* diode */ - data->temp_type[i] = 3 - ((diode >> i) & 0x02); + if ((tmp & (data->DIODE_MASK << i))) /* diode */ + data->temp_type[i] + = 3 - ((diode >> i) & data->DIODE_MASK); else /* thermistor */ data->temp_type[i] = 4; } } -static int -nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - struct nct6775_data *data) +static void +nct6775_check_fan_inputs(struct nct6775_data *data) { + bool fan3pin, fan4pin, fan4min, fan5pin, fan6pin; + bool pwm3pin, pwm4pin, pwm5pin, pwm6pin; + int sioreg = data->sioreg; int regval; - bool fan3pin, fan3min, fan4pin, fan4min, fan5pin; - bool pwm3pin, pwm4pin, pwm5pin; - int ret; - - ret = superio_enter(sio_data->sioreg); - if (ret) - return ret; /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { - regval = superio_inb(sio_data->sioreg, 0x2c); + regval = superio_inb(sioreg, 0x2c); fan3pin = regval & (1 << 6); - fan3min = fan3pin; pwm3pin = regval & (1 << 7); /* On NCT6775, fan4 shares pins with the fdc interface */ - fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); - fan4min = 0; - fan5pin = 0; - pwm4pin = 0; - pwm5pin = 0; + fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80); + fan4min = false; + fan5pin = false; + fan6pin = false; + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; } else if (data->kind == nct6776) { - bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; + bool gpok = superio_inb(sioreg, 0x27) & 0x80; - superio_select(sio_data->sioreg, NCT6775_LD_HWM); - regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); + superio_select(sioreg, NCT6775_LD_HWM); + regval = superio_inb(sioreg, SIO_REG_ENABLE); if (regval & 0x80) fan3pin = gpok; else - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); if (regval & 0x40) fan4pin = gpok; else - fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01; + fan4pin = superio_inb(sioreg, 0x1C) & 0x01; if (regval & 0x20) fan5pin = gpok; else - fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02; + fan5pin = superio_inb(sioreg, 0x1C) & 0x02; fan4min = fan4pin; - fan3min = fan3pin; + fan6pin = false; pwm3pin = fan3pin; - pwm4pin = 0; - pwm5pin = 0; - } else { /* NCT6779D */ - regval = superio_inb(sio_data->sioreg, 0x1c); + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; + } else if (data->kind == nct6106) { + regval = superio_inb(sioreg, 0x24); + fan3pin = !(regval & 0x80); + pwm3pin = regval & 0x08; + + fan4pin = false; + fan4min = false; + fan5pin = false; + fan6pin = false; + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; + } else { /* NCT6779D or NCT6791D */ + regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); fan4pin = !(regval & (1 << 6)); @@ -3368,22 +3235,25 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, pwm4pin = !(regval & (1 << 1)); pwm5pin = !(regval & (1 << 2)); - fan3min = fan3pin; fan4min = fan4pin; - } - - superio_exit(sio_data->sioreg); - data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ - data->has_fan |= fan3pin << 2; - data->has_fan_min |= fan3min << 2; - - data->has_fan |= (fan4pin << 3) | (fan5pin << 4); - data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); - - data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4); + if (data->kind == nct6791) { + regval = superio_inb(sioreg, 0x2d); + fan6pin = (regval & (1 << 1)); + pwm6pin = (regval & (1 << 0)); + } else { /* NCT6779D */ + fan6pin = false; + pwm6pin = false; + } + } - return 0; + /* fan 1 and 2 (0x03) are always present */ + data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) | + (fan5pin << 4) | (fan6pin << 5); + data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) | + (fan5pin << 4); + data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | + (pwm5pin << 4) | (pwm6pin << 5); } static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, @@ -3415,16 +3285,17 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, static int nct6775_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct nct6775_sio_data *sio_data = dev->platform_data; + struct nct6775_sio_data *sio_data = dev_get_platdata(dev); struct nct6775_data *data; struct resource *res; int i, s, err = 0; int src, mask, available; const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; const u16 *reg_temp_alternate, *reg_temp_crit; + const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL; int num_reg_temp; - bool have_vid = false; u8 cr2a; + struct attribute_group *group; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, @@ -3437,6 +3308,7 @@ static int nct6775_probe(struct platform_device *pdev) return -ENOMEM; data->kind = sio_data->kind; + data->sioreg = sio_data->sioreg; data->addr = res->start; mutex_init(&data->update_lock); data->name = nct6775_device_names[data->kind]; @@ -3444,6 +3316,75 @@ static int nct6775_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); switch (data->kind) { + case nct6106: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->temp_fixed_num = 3; + data->num_temp_alarms = 6; + data->num_temp_beeps = 6; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + + data->temp_label = nct6776_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); + + data->REG_VBAT = NCT6106_REG_VBAT; + data->REG_DIODE = NCT6106_REG_DIODE; + data->DIODE_MASK = NCT6106_DIODE_MASK; + data->REG_VIN = NCT6106_REG_IN; + data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX; + data->REG_TARGET = NCT6106_REG_TARGET; + data->REG_FAN = NCT6106_REG_FAN; + data->REG_FAN_MODE = NCT6106_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6106_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6106_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6106_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME; + data->REG_PWM[0] = NCT6106_REG_PWM; + data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6106_REG_PWM_READ; + data->REG_PWM_MODE = NCT6106_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6106_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6106_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6106_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6106_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6106_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6106_REG_ALARM; + data->ALARM_BITS = NCT6106_ALARM_BITS; + data->REG_BEEP = NCT6106_REG_BEEP; + data->BEEP_BITS = NCT6106_BEEP_BITS; + + reg_temp = NCT6106_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; + reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; + + break; case nct6775: data->in_num = 9; data->pwm_num = 3; @@ -3451,8 +3392,10 @@ static int nct6775_probe(struct platform_device *pdev) data->has_fan_div = true; data->temp_fixed_num = 3; data->num_temp_alarms = 3; + data->num_temp_beeps = 3; data->ALARM_BITS = NCT6775_ALARM_BITS; + data->BEEP_BITS = NCT6775_BEEP_BITS; data->fan_from_reg = fan_from_reg16; data->fan_from_reg_min = fan_from_reg8; @@ -3466,6 +3409,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; data->REG_VIN = NCT6775_REG_IN; data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; @@ -3474,6 +3418,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; @@ -3499,6 +3444,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6775_REG_ALARM; + data->REG_BEEP = NCT6775_REG_BEEP; reg_temp = NCT6775_REG_TEMP; num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); @@ -3516,8 +3462,10 @@ static int nct6775_probe(struct platform_device *pdev) data->has_fan_div = false; data->temp_fixed_num = 3; data->num_temp_alarms = 3; + data->num_temp_beeps = 6; data->ALARM_BITS = NCT6776_ALARM_BITS; + data->BEEP_BITS = NCT6776_BEEP_BITS; data->fan_from_reg = fan_from_reg13; data->fan_from_reg_min = fan_from_reg13; @@ -3531,6 +3479,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; data->REG_VIN = NCT6775_REG_IN; data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; @@ -3539,6 +3488,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; @@ -3564,6 +3514,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6775_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; reg_temp = NCT6775_REG_TEMP; num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); @@ -3581,8 +3532,10 @@ static int nct6775_probe(struct platform_device *pdev) data->has_fan_div = false; data->temp_fixed_num = 6; data->num_temp_alarms = 2; + data->num_temp_beeps = 2; data->ALARM_BITS = NCT6779_ALARM_BITS; + data->BEEP_BITS = NCT6779_BEEP_BITS; data->fan_from_reg = fan_from_reg13; data->fan_from_reg_min = fan_from_reg13; @@ -3596,6 +3549,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; data->REG_VIN = NCT6779_REG_IN; data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; @@ -3604,6 +3558,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; @@ -3621,6 +3576,10 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; data->REG_CRITICAL_TEMP_TOLERANCE = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6779_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; @@ -3629,6 +3588,81 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6779_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; + + reg_temp = NCT6779_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + + break; + case nct6791: + data->in_num = 15; + data->pwm_num = 6; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; + data->num_temp_alarms = 2; + data->num_temp_beeps = 2; + + data->ALARM_BITS = NCT6791_ALARM_BITS; + data->BEEP_BITS = NCT6779_BEEP_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6779_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; + data->REG_VIN = NCT6779_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6779_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6779_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6791_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; reg_temp = NCT6779_REG_TEMP; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); @@ -3700,6 +3734,13 @@ static int nct6775_probe(struct platform_device *pdev) data->reg_temp[0][src - 1] = reg_temp[i]; data->reg_temp[1][src - 1] = reg_temp_over[i]; data->reg_temp[2][src - 1] = reg_temp_hyst[i]; + if (reg_temp_crit_h && reg_temp_crit_h[i]) + data->reg_temp[3][src - 1] = reg_temp_crit_h[i]; + else if (reg_temp_crit[src - 1]) + data->reg_temp[3][src - 1] + = reg_temp_crit[src - 1]; + if (reg_temp_crit_l && reg_temp_crit_l[i]) + data->reg_temp[4][src - 1] = reg_temp_crit_l[i]; data->reg_temp_config[src - 1] = reg_temp_config[i]; data->temp_src[src - 1] = src; continue; @@ -3714,8 +3755,12 @@ static int nct6775_probe(struct platform_device *pdev) data->reg_temp[1][s] = reg_temp_over[i]; data->reg_temp[2][s] = reg_temp_hyst[i]; data->reg_temp_config[s] = reg_temp_config[i]; - if (reg_temp_crit[src - 1]) + if (reg_temp_crit_h && reg_temp_crit_h[i]) + data->reg_temp[3][s] = reg_temp_crit_h[i]; + else if (reg_temp_crit[src - 1]) data->reg_temp[3][s] = reg_temp_crit[src - 1]; + if (reg_temp_crit_l && reg_temp_crit_l[i]) + data->reg_temp[4][s] = reg_temp_crit_l[i]; data->temp_src[s] = src; s++; @@ -3767,12 +3812,14 @@ static int nct6775_probe(struct platform_device *pdev) cr2a = superio_inb(sio_data->sioreg, 0x2a); switch (data->kind) { case nct6775: - have_vid = (cr2a & 0x40); + data->have_vid = (cr2a & 0x40); break; case nct6776: - have_vid = (cr2a & 0x60) == 0x40; + data->have_vid = (cr2a & 0x60) == 0x40; break; + case nct6106: case nct6779: + case nct6791: break; } @@ -3780,7 +3827,7 @@ static int nct6775_probe(struct platform_device *pdev) * Read VID value * We can get the VID input values directly at logical device D 0xe3. */ - if (have_vid) { + if (data->have_vid) { superio_select(sio_data->sioreg, NCT6775_LD_VID); data->vid = superio_inb(sio_data->sioreg, 0xe3); data->vrm = vid_which_vrm(); @@ -3793,6 +3840,9 @@ static int nct6775_probe(struct platform_device *pdev) tmp = superio_inb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE); switch (data->kind) { + case nct6106: + tmp |= 0xe0; + break; case nct6775: tmp |= 0x1e; break; @@ -3800,6 +3850,9 @@ static int nct6775_probe(struct platform_device *pdev) case nct6779: tmp |= 0x3e; break; + case nct6791: + tmp |= 0x7e; + break; } superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE, tmp); @@ -3807,157 +3860,47 @@ static int nct6775_probe(struct platform_device *pdev) data->name); } - superio_exit(sio_data->sioreg); - - if (have_vid) { - err = device_create_file(dev, &dev_attr_cpu0_vid); - if (err) - return err; - } + nct6775_check_fan_inputs(data); - err = nct6775_check_fan_inputs(sio_data, data); - if (err) - goto exit_remove; + superio_exit(sio_data->sioreg); /* Read fan clock dividers immediately */ nct6775_init_fan_common(dev, data); /* Register sysfs hooks */ - for (i = 0; i < data->pwm_num; i++) { - if (!(data->has_pwm & (1 << i))) - continue; - - err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]); - if (err) - goto exit_remove; - - if (data->REG_PWM[3]) { - err = device_create_file(dev, - &sda_pwm_max[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->REG_PWM[4]) { - err = device_create_file(dev, - &sda_pwm_step[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->REG_PWM[6]) { - err = device_create_file(dev, - &sda_weight_duty_base[i].dev_attr); - if (err) - goto exit_remove; - } - } - for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) { - struct sensor_device_attribute_2 *attr = - &sda_auto_pwm_arrays[i]; - - if (!(data->has_pwm & (1 << attr->nr))) - continue; - if (attr->index > data->auto_pwm_num) - continue; - err = device_create_file(dev, &attr->dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < data->in_num; i++) { - if (!(data->have_in & (1 << i))) - continue; - err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]); - if (err) - goto exit_remove; + group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group, + data->pwm_num); + if (IS_ERR(group)) { + err = PTR_ERR(group); + goto exit_remove; } + data->group_pwm = group; - for (i = 0; i < 5; i++) { - if (data->has_fan & (1 << i)) { - err = device_create_file(dev, - &sda_fan_input[i].dev_attr); - if (err) - goto exit_remove; - if (data->ALARM_BITS[FAN_ALARM_BASE + i] >= 0) { - err = device_create_file(dev, - &sda_fan_alarm[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->kind != nct6776 && - data->kind != nct6779) { - err = device_create_file(dev, - &sda_fan_div[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->has_fan_min & (1 << i)) { - err = device_create_file(dev, - &sda_fan_min[i].dev_attr); - if (err) - goto exit_remove; - } - err = device_create_file(dev, - &sda_fan_pulses[i].dev_attr); - if (err) - goto exit_remove; - } + group = nct6775_create_attr_group(dev, &nct6775_in_template_group, + fls(data->have_in)); + if (IS_ERR(group)) { + err = PTR_ERR(group); + goto exit_remove; } + data->group_in = group; - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; - err = device_create_file(dev, &sda_temp_input[i].dev_attr); - if (err) - goto exit_remove; - if (data->temp_label) { - err = device_create_file(dev, - &sda_temp_label[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->reg_temp[1][i]) { - err = device_create_file(dev, - &sda_temp_max[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->reg_temp[2][i]) { - err = device_create_file(dev, - &sda_temp_max_hyst[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->reg_temp[3][i]) { - err = device_create_file(dev, - &sda_temp_crit[i].dev_attr); - if (err) - goto exit_remove; - } - if (find_temp_source(data, i, data->num_temp_alarms) >= 0) { - err = device_create_file(dev, - &sda_temp_alarm[i].dev_attr); - if (err) - goto exit_remove; - } - if (!(data->have_temp_fixed & (1 << i))) - continue; - err = device_create_file(dev, &sda_temp_type[i].dev_attr); - if (err) - goto exit_remove; - err = device_create_file(dev, &sda_temp_offset[i].dev_attr); - if (err) - goto exit_remove; + group = nct6775_create_attr_group(dev, &nct6775_fan_template_group, + fls(data->has_fan)); + if (IS_ERR(group)) { + err = PTR_ERR(group); + goto exit_remove; } + data->group_fan = group; - for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) { - if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0) - continue; - err = device_create_file(dev, &sda_caseopen[i].dev_attr); - if (err) - goto exit_remove; + group = nct6775_create_attr_group(dev, &nct6775_temp_template_group, + fls(data->have_temp)); + if (IS_ERR(group)) { + err = PTR_ERR(group); + goto exit_remove; } + data->group_temp = group; - err = device_create_file(dev, &dev_attr_name); + err = sysfs_create_group(&dev->kobj, &nct6775_group_other); if (err) goto exit_remove; @@ -3988,11 +3931,10 @@ static int nct6775_remove(struct platform_device *pdev) static int nct6775_suspend(struct device *dev) { struct nct6775_data *data = nct6775_update_device(dev); - struct nct6775_sio_data *sio_data = dev->platform_data; mutex_lock(&data->update_lock); data->vbat = nct6775_read_value(data, data->REG_VBAT); - if (sio_data->kind == nct6775) { + if (data->kind == nct6775) { data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1); data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2); } @@ -4004,7 +3946,6 @@ static int nct6775_suspend(struct device *dev) static int nct6775_resume(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); - struct nct6775_sio_data *sio_data = dev->platform_data; int i, j; mutex_lock(&data->update_lock); @@ -4041,7 +3982,7 @@ static int nct6775_resume(struct device *dev) /* Restore other settings */ nct6775_write_value(data, data->REG_VBAT, data->vbat); - if (sio_data->kind == nct6775) { + if (data->kind == nct6775) { nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); } @@ -4056,6 +3997,8 @@ static int nct6775_resume(struct device *dev) static const struct dev_pm_ops nct6775_dev_pm_ops = { .suspend = nct6775_suspend, .resume = nct6775_resume, + .freeze = nct6775_suspend, + .restore = nct6775_resume, }; #define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) @@ -4074,17 +4017,19 @@ static struct platform_driver nct6775_driver = { }; static const char * const nct6775_sio_names[] __initconst = { + "NCT6106D", "NCT6775F", "NCT6776D/F", "NCT6779D", + "NCT6791D", }; /* nct6775_find() looks for a '627 in the Super-I/O config space */ -static int __init nct6775_find(int sioaddr, unsigned short *addr, - struct nct6775_sio_data *sio_data) +static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) { u16 val; int err; + int addr; err = superio_enter(sioaddr); if (err) @@ -4096,6 +4041,9 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | superio_inb(sioaddr, SIO_REG_DEVID + 1); switch (val & SIO_ID_MASK) { + case SIO_NCT6106_ID: + sio_data->kind = nct6106; + break; case SIO_NCT6775_ID: sio_data->kind = nct6775; break; @@ -4105,6 +4053,9 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, case SIO_NCT6779_ID: sio_data->kind = nct6779; break; + case SIO_NCT6791_ID: + sio_data->kind = nct6791; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4116,8 +4067,8 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, superio_select(sioaddr, NCT6775_LD_HWM); val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) | superio_inb(sioaddr, SIO_REG_ADDR + 1); - *addr = val & IOREGION_ALIGNMENT; - if (*addr == 0) { + addr = val & IOREGION_ALIGNMENT; + if (addr == 0) { pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); superio_exit(sioaddr); return -ENODEV; @@ -4129,13 +4080,22 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } + if (sio_data->kind == nct6791) { + val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); + if (val & 0x10) { + pr_info("Enabling hardware monitor logical device mappings.\n"); + superio_outb(sioaddr, + NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, + val & ~0x10); + } + } superio_exit(sioaddr); - pr_info("Found %s or compatible chip at %#x\n", - nct6775_sio_names[sio_data->kind], *addr); + pr_info("Found %s or compatible chip at %#x:%#x\n", + nct6775_sio_names[sio_data->kind], sioaddr, addr); sio_data->sioreg = sioaddr; - return 0; + return addr; } /* @@ -4144,14 +4104,20 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, * track of the nct6775 driver. But since we platform_device_alloc(), we * must keep track of the device */ -static struct platform_device *pdev; +static struct platform_device *pdev[2]; static int __init sensors_nct6775_init(void) { - int err; - unsigned short address; + int i, err; + bool found = false; + int address; struct resource res; struct nct6775_sio_data sio_data; + int sioaddr[2] = { 0x2e, 0x4e }; + + err = platform_driver_register(&nct6775_driver); + if (err) + return err; /* * initialize sio_data->kind and sio_data->sioreg. @@ -4160,64 +4126,71 @@ static int __init sensors_nct6775_init(void) * driver will probe 0x2e and 0x4e and auto-detect the presence of a * nct6775 hardware monitor, and call probe() */ - if (nct6775_find(0x2e, &address, &sio_data) && - nct6775_find(0x4e, &address, &sio_data)) - return -ENODEV; - - err = platform_driver_register(&nct6775_driver); - if (err) - goto exit; + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + address = nct6775_find(sioaddr[i], &sio_data); + if (address <= 0) + continue; - pdev = platform_device_alloc(DRVNAME, address); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit_unregister; - } + found = true; - err = platform_device_add_data(pdev, &sio_data, - sizeof(struct nct6775_sio_data)); - if (err) { - pr_err("Platform data allocation failed\n"); - goto exit_device_put; - } + pdev[i] = platform_device_alloc(DRVNAME, address); + if (!pdev[i]) { + err = -ENOMEM; + goto exit_device_put; + } - memset(&res, 0, sizeof(res)); - res.name = DRVNAME; - res.start = address + IOREGION_OFFSET; - res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; - res.flags = IORESOURCE_IO; + err = platform_device_add_data(pdev[i], &sio_data, + sizeof(struct nct6775_sio_data)); + if (err) + goto exit_device_put; + + memset(&res, 0, sizeof(res)); + res.name = DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) { + platform_device_put(pdev[i]); + pdev[i] = NULL; + continue; + } - err = acpi_check_resource_conflict(&res); - if (err) - goto exit_device_put; + err = platform_device_add_resources(pdev[i], &res, 1); + if (err) + goto exit_device_put; - err = platform_device_add_resources(pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed (%d)\n", err); - goto exit_device_put; + /* platform_device_add calls probe() */ + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_put; } - - /* platform_device_add calls probe() */ - err = platform_device_add(pdev); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; + if (!found) { + err = -ENODEV; + goto exit_unregister; } return 0; exit_device_put: - platform_device_put(pdev); + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + if (pdev[i]) + platform_device_put(pdev[i]); + } exit_unregister: platform_driver_unregister(&nct6775_driver); -exit: return err; } static void __exit sensors_nct6775_exit(void) { - platform_device_unregister(pdev); + int i; + + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + if (pdev[i]) + platform_device_unregister(pdev[i]); + } platform_driver_unregister(&nct6775_driver); } diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 830a842..8c23203 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -424,7 +424,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) if (IS_ERR(pdata)) return PTR_ERR(pdata); else if (pdata == NULL) - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform init data supplied.\n"); diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index ea60686..6e6ea44 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -983,7 +983,7 @@ static int pc87427_request_regions(struct platform_device *pdev, static void pc87427_init_device(struct device *dev) { - struct pc87427_sio_data *sio_data = dev->platform_data; + struct pc87427_sio_data *sio_data = dev_get_platdata(dev); struct pc87427_data *data = dev_get_drvdata(dev); int i; u8 reg; @@ -1075,7 +1075,7 @@ static void pc87427_remove_files(struct device *dev) static int pc87427_probe(struct platform_device *pdev) { - struct pc87427_sio_data *sio_data = pdev->dev.platform_data; + struct pc87427_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct pc87427_data *data; int i, err, res_count; diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 9add6092..9319fcf 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1726,7 +1726,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { struct device *dev = &client->dev; - const struct pmbus_platform_data *pdata = dev->platform_data; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); struct pmbus_data *data; int ret; diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index a9f7e80..73bd64e 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -165,7 +165,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev, { struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); struct s3c_hwmon *hwmon = platform_get_drvdata(to_platform_device(dev)); - struct s3c_hwmon_pdata *pdata = dev->platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev); struct s3c_hwmon_chcfg *cfg; int ret; @@ -194,7 +194,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev, char *buf) { struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); - struct s3c_hwmon_pdata *pdata = dev->platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev); struct s3c_hwmon_chcfg *cfg; cfg = pdata->in[sen_attr->index]; @@ -274,7 +274,7 @@ static void s3c_hwmon_remove_attr(struct device *dev, */ static int s3c_hwmon_probe(struct platform_device *dev) { - struct s3c_hwmon_pdata *pdata = dev->dev.platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(&dev->dev); struct s3c_hwmon *hwmon; int ret = 0; int i; diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 2507f90..97cd45a 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -940,11 +940,11 @@ static int sht15_probe(struct platform_device *pdev) data->dev = &pdev->dev; init_waitqueue_head(&data->wait_queue); - if (pdev->dev.platform_data == NULL) { + if (dev_get_platdata(&pdev->dev) == NULL) { dev_err(&pdev->dev, "no platform data supplied\n"); return -EINVAL; } - data->pdata = pdev->dev.platform_data; + data->pdata = dev_get_platdata(&pdev->dev); data->supply_uv = data->pdata->supply_mv * 1000; if (data->pdata->checksum) data->checksumming = true; @@ -957,7 +957,7 @@ static int sht15_probe(struct platform_device *pdev) * If a regulator is available, * query what the supply voltage actually is! */ - data->reg = devm_regulator_get(data->dev, "vcc"); + data->reg = devm_regulator_get_optional(data->dev, "vcc"); if (!IS_ERR(data->reg)) { int voltage; diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 6d8255c..05cb814 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -668,7 +668,7 @@ static void smsc47m1_remove_files(struct device *dev) static int __init smsc47m1_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct smsc47m1_sio_data *sio_data = dev->platform_data; + struct smsc47m1_sio_data *sio_data = dev_get_platdata(dev); struct smsc47m1_data *data; struct resource *res; int err; @@ -940,7 +940,7 @@ exit_device: static void __exit sm_smsc47m1_exit(void) { platform_driver_unregister(&smsc47m1_driver); - smsc47m1_restore(pdev->dev.platform_data); + smsc47m1_restore(dev_get_platdata(&pdev->dev)); platform_device_unregister(pdev); } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 004801e..23ff210 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -673,7 +673,7 @@ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) static void w83627ehf_write_fan_div_common(struct device *dev, struct w83627ehf_data *data, int nr) { - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); if (sio_data->kind == nct6776) ; /* no dividers, do nothing */ @@ -724,7 +724,7 @@ static void w83627ehf_update_fan_div(struct w83627ehf_data *data) static void w83627ehf_update_fan_div_common(struct device *dev, struct w83627ehf_data *data) { - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); if (sio_data->kind == nct6776) ; /* no dividers, do nothing */ @@ -781,7 +781,7 @@ static void w83627ehf_update_pwm(struct w83627ehf_data *data) static void w83627ehf_update_pwm_common(struct device *dev, struct w83627ehf_data *data) { - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); if (sio_data->kind == nct6775 || sio_data->kind == nct6776) nct6775_update_pwm(data); @@ -792,7 +792,7 @@ static void w83627ehf_update_pwm_common(struct device *dev, static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); int i; @@ -1392,7 +1392,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, { struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); int nr = sensor_attr->index; unsigned long val; int err; @@ -1448,7 +1448,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; unsigned long val; @@ -1527,7 +1527,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; u16 reg; @@ -2065,7 +2065,7 @@ w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, static int w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct w83627ehf_data *data; struct resource *res; u8 en_vrm10; @@ -2618,7 +2618,7 @@ static int w83627ehf_remove(struct platform_device *pdev) static int w83627ehf_suspend(struct device *dev) { struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); mutex_lock(&data->update_lock); data->vbat = w83627ehf_read_value(data, W83627EHF_REG_VBAT); @@ -2634,7 +2634,7 @@ static int w83627ehf_suspend(struct device *dev) static int w83627ehf_resume(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); int i; mutex_lock(&data->update_lock); @@ -2694,6 +2694,8 @@ static int w83627ehf_resume(struct device *dev) static const struct dev_pm_ops w83627ehf_dev_pm_ops = { .suspend = w83627ehf_suspend, .resume = w83627ehf_resume, + .freeze = w83627ehf_suspend, + .restore = w83627ehf_resume, }; #define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops) diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 3b9ef2d..cb9cd32 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1415,7 +1415,7 @@ static const struct attribute_group w83627hf_group_opt = { static int w83627hf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct w83627hf_sio_data *sio_data = dev->platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(dev); struct w83627hf_data *data; struct resource *res; int err, i; @@ -1636,7 +1636,7 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) static int w83627thf_read_gpio5(struct platform_device *pdev) { - struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff, sel; superio_enter(sio_data); @@ -1669,7 +1669,7 @@ exit: static int w83687thf_read_vid(struct platform_device *pdev) { - struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff; superio_enter(sio_data); diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 5f4749e..c1cd569 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -232,7 +232,8 @@ static int adjd_s311_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = adjd_s311_read_data(indio_dev, chan->address, val); + ret = adjd_s311_read_data(indio_dev, + ADJD_S311_DATA_REG(chan->address), val); if (ret < 0) return ret; return IIO_VAL_INT; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index fa061d4..75e3b10 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -167,6 +167,7 @@ static const struct xpad_device { { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd00, "Razer Onza Tournament Edition", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1689, 0xfd01, "Razer Onza Classic Edition", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 57b2637..8551dca 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -672,6 +672,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse) */ static int elantech_packet_check_v3(struct psmouse *psmouse) { + struct elantech_data *etd = psmouse->private; const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff }; unsigned char *packet = psmouse->packet; @@ -682,19 +683,48 @@ static int elantech_packet_check_v3(struct psmouse *psmouse) if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) return PACKET_DEBOUNCE; - if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) - return PACKET_V3_HEAD; + /* + * If the hardware flag 'crc_enabled' is set the packets have + * different signatures. + */ + if (etd->crc_enabled) { + if ((packet[3] & 0x09) == 0x08) + return PACKET_V3_HEAD; + + if ((packet[3] & 0x09) == 0x09) + return PACKET_V3_TAIL; + } else { + if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) + return PACKET_V3_HEAD; - if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) - return PACKET_V3_TAIL; + if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) + return PACKET_V3_TAIL; + } return PACKET_UNKNOWN; } static int elantech_packet_check_v4(struct psmouse *psmouse) { + struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; unsigned char packet_type = packet[3] & 0x03; + bool sanity_check; + + /* + * Sanity check based on the constant bits of a packet. + * The constant bits change depending on the value of + * the hardware flag 'crc_enabled' but are the same for + * every packet, regardless of the type. + */ + if (etd->crc_enabled) + sanity_check = ((packet[3] & 0x08) == 0x00); + else + sanity_check = ((packet[0] & 0x0c) == 0x04 && + (packet[3] & 0x1c) == 0x10); + + if (!sanity_check) + return PACKET_UNKNOWN; switch (packet_type) { case 0: @@ -1313,6 +1343,12 @@ static int elantech_set_properties(struct elantech_data *etd) etd->reports_pressure = true; } + /* + * The signatures of v3 and v4 packets change depending on the + * value of this hardware flag. + */ + etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000); + return 0; } diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 46db3be..036a04a 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -129,6 +129,7 @@ struct elantech_data { bool paritycheck; bool jumpy_cursor; bool reports_pressure; + bool crc_enabled; unsigned char hw_version; unsigned int fw_version; unsigned int single_finger_reports; diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 94c17c2..1e691a3 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -22,7 +22,8 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EXPERT || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ - (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 + (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \ + !ARC help i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 384fbcd..f3e91f0 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -2112,7 +2112,7 @@ static const struct wacom_features wacom_features_0xDA = { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; -static struct wacom_features wacom_features_0xDB = +static const struct wacom_features wacom_features_0xDB = { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; @@ -2127,6 +2127,12 @@ static const struct wacom_features wacom_features_0xDF = { "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0x300 = + { "Wacom Bamboo One S", WACOM_PKGLEN_BBPEN, 14720, 9225, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x301 = + { "Wacom Bamboo One M", WACOM_PKGLEN_BBPEN, 21648, 13530, 1023, + 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x6004 = { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2253,6 +2259,8 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x100) }, { USB_DEVICE_WACOM(0x101) }, { USB_DEVICE_WACOM(0x10D) }, + { USB_DEVICE_WACOM(0x300) }, + { USB_DEVICE_WACOM(0x301) }, { USB_DEVICE_WACOM(0x304) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x47) }, diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 69ea44e..4851afa 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -23,7 +23,7 @@ #define SIRFSOC_INT_RISC_LEVEL1 0x0024 #define SIRFSOC_INIT_IRQ_ID 0x0038 -#define SIRFSOC_NUM_IRQS 128 +#define SIRFSOC_NUM_IRQS 64 static struct irq_domain *sirfsoc_irqdomain; @@ -32,15 +32,18 @@ sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; + int ret; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; - gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq); - ct = gc->chip_types; + ret = irq_alloc_domain_generic_chips(sirfsoc_irqdomain, num, 1, "irq_sirfsoc", + handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); + gc = irq_get_domain_generic_chip(sirfsoc_irqdomain, irq_start); + gc->reg_base = base; + ct = gc->chip_types; ct->chip.irq_mask = irq_gc_mask_clr_bit; ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->regs.mask = SIRFSOC_INT_RISC_MASK0; - - irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0); } static asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) @@ -60,9 +63,8 @@ static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *p if (!base) panic("unable to map intc cpu registers\n"); - /* using legacy because irqchip_generic does not work with linear */ - sirfsoc_irqdomain = irq_domain_add_legacy(np, SIRFSOC_NUM_IRQS, 0, 0, - &irq_domain_simple_ops, base); + sirfsoc_irqdomain = irq_domain_add_linear(np, SIRFSOC_NUM_IRQS, + &irq_generic_chip_ops, base); sirfsoc_alloc_gc(base, 0, 32); sirfsoc_alloc_gc(base + 4, 32, SIRFSOC_NUM_IRQS - 32); diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 22b720e..77025f5 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -288,8 +288,10 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) u8 *data; int len; - if (skb->len < sizeof(int)) + if (skb->len < sizeof(int)) { printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); + return -EINVAL; + } cont = *((int *)skb->data); len = skb->len - sizeof(int); data = skb->data + sizeof(int); diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index dc112a7..4296155 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -959,23 +959,21 @@ out: return r; } -static void remove_mapping(struct mq_policy *mq, dm_oblock_t oblock) +static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock) { - struct entry *e = hash_lookup(mq, oblock); + struct mq_policy *mq = to_mq_policy(p); + struct entry *e; + + mutex_lock(&mq->lock); + + e = hash_lookup(mq, oblock); BUG_ON(!e || !e->in_cache); del(mq, e); e->in_cache = false; push(mq, e); -} -static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock) -{ - struct mq_policy *mq = to_mq_policy(p); - - mutex_lock(&mq->lock); - remove_mapping(mq, oblock); mutex_unlock(&mq->lock); } diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 0029536..28f51e0 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -2,7 +2,7 @@ * A driver for the Integrated Circuits ICS932S401 * Copyright (C) 2008 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -482,7 +482,7 @@ static int ics932s401_remove(struct i2c_client *client) module_i2c_driver(ics932s401_driver); -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ICS932S401 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 49a5bca..5d08855 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1313,7 +1313,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) supply = devm_regulator_get(dev, "vmmc"); mmc->supply.vmmc = supply; - mmc->supply.vqmmc = devm_regulator_get(dev, "vqmmc"); + mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc"); if (IS_ERR(supply)) return PTR_ERR(supply); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ee5f167..5424073 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2231,7 +2231,7 @@ int dw_mci_probe(struct dw_mci *host) } } - host->vmmc = devm_regulator_get(host->dev, "vmmc"); + host->vmmc = devm_regulator_get_optional(host->dev, "vmmc"); if (IS_ERR(host->vmmc)) { ret = PTR_ERR(host->vmmc); if (ret == -EPROBE_DEFER) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 2c5a91b..1956a3d 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -83,7 +83,7 @@ struct pxamci_host { static inline void pxamci_init_ocr(struct pxamci_host *host) { #ifdef CONFIG_REGULATOR - host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc"); + host->vcc = regulator_get_optional(mmc_dev(host->mmc), "vmmc"); if (IS_ERR(host->vcc)) host->vcc = NULL; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a78bd4f..dd2c083 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2966,7 +2966,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ - host->vqmmc = regulator_get(mmc_dev(mmc), "vqmmc"); + host->vqmmc = regulator_get_optional(mmc_dev(mmc), "vqmmc"); if (IS_ERR_OR_NULL(host->vqmmc)) { if (PTR_ERR(host->vqmmc) < 0) { pr_info("%s: no vqmmc regulator found\n", @@ -3042,7 +3042,7 @@ int sdhci_add_host(struct sdhci_host *host) ocr_avail = 0; - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + host->vmmc = regulator_get_optional(mmc_dev(mmc), "vmmc"); if (IS_ERR_OR_NULL(host->vmmc)) { if (PTR_ERR(host->vmmc) < 0) { pr_info("%s: no vmmc regulator found\n", diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index ce9b387..00b88cb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1333,6 +1333,8 @@ enum { BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN, BNX2X_SP_RTNL_VFPF_STORM_RX_MODE, BNX2X_SP_RTNL_HYPERVISOR_VLAN, + BNX2X_SP_RTNL_TX_STOP, + BNX2X_SP_RTNL_TX_RESUME, }; struct bnx2x_prev_path_list { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f2d1ff1..0cc2611 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -53,6 +53,7 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) struct bnx2x_fp_stats *to_fp_stats = &bp->fp_stats[to]; int old_max_eth_txqs, new_max_eth_txqs; int old_txdata_index = 0, new_txdata_index = 0; + struct bnx2x_agg_info *old_tpa_info = to_fp->tpa_info; /* Copy the NAPI object as it has been already initialized */ from_fp->napi = to_fp->napi; @@ -61,6 +62,11 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) memcpy(to_fp, from_fp, sizeof(*to_fp)); to_fp->index = to; + /* Retain the tpa_info of the original `to' version as we don't want + * 2 FPs to contain the same tpa_info pointer. + */ + to_fp->tpa_info = old_tpa_info; + /* move sp_objs contents as well, as their indices match fp ones */ memcpy(to_sp_objs, from_sp_objs, sizeof(*to_sp_objs)); @@ -2956,8 +2962,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) if (IS_PF(bp)) { if (CNIC_LOADED(bp)) bnx2x_free_mem_cnic(bp); - bnx2x_free_mem(bp); } + bnx2x_free_mem(bp); + bp->state = BNX2X_STATE_CLOSED; bp->cnic_loaded = false; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index f9122f2..fcf2761 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -30,10 +30,8 @@ #include "bnx2x_dcb.h" /* forward declarations of dcbx related functions */ -static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp); static void bnx2x_pfc_set_pfc(struct bnx2x *bp); static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp); -static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp); static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp, u32 *set_configuration_ets_pg, u32 *pri_pg_tbl); @@ -425,30 +423,52 @@ static void bnx2x_pfc_set_pfc(struct bnx2x *bp) bnx2x_pfc_clear(bp); } -static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp) +int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp) { struct bnx2x_func_state_params func_params = {NULL}; + int rc; func_params.f_obj = &bp->func_obj; func_params.cmd = BNX2X_F_CMD_TX_STOP; + __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags); + __set_bit(RAMROD_RETRY, &func_params.ramrod_flags); + DP(BNX2X_MSG_DCB, "STOP TRAFFIC\n"); - return bnx2x_func_state_change(bp, &func_params); + + rc = bnx2x_func_state_change(bp, &func_params); + if (rc) { + BNX2X_ERR("Unable to hold traffic for HW configuration\n"); + bnx2x_panic(); + } + + return rc; } -static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp) +int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp) { struct bnx2x_func_state_params func_params = {NULL}; struct bnx2x_func_tx_start_params *tx_params = &func_params.params.tx_start; + int rc; func_params.f_obj = &bp->func_obj; func_params.cmd = BNX2X_F_CMD_TX_START; + __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags); + __set_bit(RAMROD_RETRY, &func_params.ramrod_flags); + bnx2x_dcbx_fw_struct(bp, tx_params); DP(BNX2X_MSG_DCB, "START TRAFFIC\n"); - return bnx2x_func_state_change(bp, &func_params); + + rc = bnx2x_func_state_change(bp, &func_params); + if (rc) { + BNX2X_ERR("Unable to resume traffic after HW configuration\n"); + bnx2x_panic(); + } + + return rc; } static void bnx2x_dcbx_2cos_limit_update_ets_config(struct bnx2x *bp) @@ -744,7 +764,9 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) if (IS_MF(bp)) bnx2x_link_sync_notify(bp); - bnx2x_dcbx_stop_hw_tx(bp); + set_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state); + + schedule_delayed_work(&bp->sp_rtnl_task, 0); return; } @@ -757,7 +779,9 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) /* ets may affect cmng configuration: reinit it in hw */ bnx2x_set_local_cmng(bp); - bnx2x_dcbx_resume_hw_tx(bp); + set_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state); + + schedule_delayed_work(&bp->sp_rtnl_task, 0); return; case BNX2X_DCBX_STATE_TX_RELEASED: @@ -2367,21 +2391,24 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid, case DCB_FEATCFG_ATTR_PG: if (bp->dcbx_local_feat.ets.enabled) *flags |= DCB_FEATCFG_ENABLE; - if (bp->dcbx_error & DCBX_LOCAL_ETS_ERROR) + if (bp->dcbx_error & (DCBX_LOCAL_ETS_ERROR | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; case DCB_FEATCFG_ATTR_PFC: if (bp->dcbx_local_feat.pfc.enabled) *flags |= DCB_FEATCFG_ENABLE; if (bp->dcbx_error & (DCBX_LOCAL_PFC_ERROR | - DCBX_LOCAL_PFC_MISMATCH)) + DCBX_LOCAL_PFC_MISMATCH | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; case DCB_FEATCFG_ATTR_APP: if (bp->dcbx_local_feat.app.enabled) *flags |= DCB_FEATCFG_ENABLE; if (bp->dcbx_error & (DCBX_LOCAL_APP_ERROR | - DCBX_LOCAL_APP_MISMATCH)) + DCBX_LOCAL_APP_MISMATCH | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; default: diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h index 125bd1b..804b8f6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h @@ -199,4 +199,7 @@ extern const struct dcbnl_rtnl_ops bnx2x_dcbnl_ops; int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall); #endif /* BCM_DCBNL */ +int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp); +int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp); + #endif /* BNX2X_DCB_H */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 955d6cf..1627a4e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -2261,6 +2261,23 @@ static void bnx2x_set_requested_fc(struct bnx2x *bp) bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH; } +static void bnx2x_init_dropless_fc(struct bnx2x *bp) +{ + u32 pause_enabled = 0; + + if (!CHIP_IS_E1(bp) && bp->dropless_fc && bp->link_vars.link_up) { + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) + pause_enabled = 1; + + REG_WR(bp, BAR_USTRORM_INTMEM + + USTORM_ETH_PAUSE_ENABLED_OFFSET(BP_PORT(bp)), + pause_enabled); + } + + DP(NETIF_MSG_IFUP | NETIF_MSG_LINK, "dropless_fc is %s\n", + pause_enabled ? "enabled" : "disabled"); +} + int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode) { int rc, cfx_idx = bnx2x_get_link_cfg_idx(bp); @@ -2294,6 +2311,8 @@ int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode) bnx2x_release_phy_lock(bp); + bnx2x_init_dropless_fc(bp); + bnx2x_calc_fc_adv(bp); if (bp->link_vars.link_up) { @@ -2315,6 +2334,8 @@ void bnx2x_link_set(struct bnx2x *bp) bnx2x_phy_init(&bp->link_params, &bp->link_vars); bnx2x_release_phy_lock(bp); + bnx2x_init_dropless_fc(bp); + bnx2x_calc_fc_adv(bp); } else BNX2X_ERR("Bootcode is missing - can not set link\n"); @@ -2556,20 +2577,9 @@ static void bnx2x_link_attn(struct bnx2x *bp) bnx2x_link_update(&bp->link_params, &bp->link_vars); - if (bp->link_vars.link_up) { + bnx2x_init_dropless_fc(bp); - /* dropless flow control */ - if (!CHIP_IS_E1(bp) && bp->dropless_fc) { - int port = BP_PORT(bp); - u32 pause_enabled = 0; - - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) - pause_enabled = 1; - - REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_ETH_PAUSE_ENABLED_OFFSET(port), - pause_enabled); - } + if (bp->link_vars.link_up) { if (bp->link_vars.mac_type != MAC_TYPE_EMAC) { struct host_port_stats *pstats; @@ -7845,12 +7855,15 @@ void bnx2x_free_mem(struct bnx2x *bp) { int i; - BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping, - sizeof(struct host_sp_status_block)); - BNX2X_PCI_FREE(bp->fw_stats, bp->fw_stats_mapping, bp->fw_stats_data_sz + bp->fw_stats_req_sz); + if (IS_VF(bp)) + return; + + BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping, + sizeof(struct host_sp_status_block)); + BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping, sizeof(struct bnx2x_slowpath)); @@ -9645,6 +9658,12 @@ sp_rtnl_not_reset: &bp->sp_rtnl_state)) bnx2x_pf_set_vfs_vlan(bp); + if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state)) + bnx2x_dcbx_stop_hw_tx(bp); + + if (test_and_clear_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state)) + bnx2x_dcbx_resume_hw_tx(bp); + /* work which needs rtnl lock not-taken (as it takes the lock itself and * can be called from other contexts as well) */ @@ -11147,6 +11166,9 @@ static bool bnx2x_get_dropless_info(struct bnx2x *bp) int tmp; u32 cfg; + if (IS_VF(bp)) + return 0; + if (IS_MF(bp) && !CHIP_IS_E1x(bp)) { /* Take function: tmp = func */ tmp = BP_ABS_FUNC(bp); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 44104fb..e8706e1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -522,23 +522,6 @@ static int bnx2x_vfop_set_user_req(struct bnx2x *bp, return 0; } -static int -bnx2x_vfop_config_vlan0(struct bnx2x *bp, - struct bnx2x_vlan_mac_ramrod_params *vlan_mac, - bool add) -{ - int rc; - - vlan_mac->user_req.cmd = add ? BNX2X_VLAN_MAC_ADD : - BNX2X_VLAN_MAC_DEL; - vlan_mac->user_req.u.vlan.vlan = 0; - - rc = bnx2x_config_vlan_mac(bp, vlan_mac); - if (rc == -EEXIST) - rc = 0; - return rc; -} - static int bnx2x_vfop_config_list(struct bnx2x *bp, struct bnx2x_vfop_filters *filters, struct bnx2x_vlan_mac_ramrod_params *vlan_mac) @@ -643,30 +626,14 @@ static void bnx2x_vfop_vlan_mac(struct bnx2x *bp, struct bnx2x_virtf *vf) case BNX2X_VFOP_VLAN_CONFIG_LIST: /* next state */ - vfop->state = BNX2X_VFOP_VLAN_CONFIG_LIST_0; - - /* remove vlan0 - could be no-op */ - vfop->rc = bnx2x_vfop_config_vlan0(bp, vlan_mac, false); - if (vfop->rc) - goto op_err; + vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE; - /* Do vlan list config. if this operation fails we try to - * restore vlan0 to keep the queue is working order - */ + /* do list config */ vfop->rc = bnx2x_vfop_config_list(bp, filters, vlan_mac); if (!vfop->rc) { set_bit(RAMROD_CONT, &vlan_mac->ramrod_flags); vfop->rc = bnx2x_config_vlan_mac(bp, vlan_mac); } - bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT); /* fall-through */ - - case BNX2X_VFOP_VLAN_CONFIG_LIST_0: - /* next state */ - vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE; - - if (list_empty(&obj->head)) - /* add vlan0 */ - vfop->rc = bnx2x_vfop_config_vlan0(bp, vlan_mac, true); bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE); default: @@ -1747,11 +1714,8 @@ void bnx2x_iov_init_dq(struct bnx2x *bp) void bnx2x_iov_init_dmae(struct bnx2x *bp) { - DP(BNX2X_MSG_IOV, "SRIOV is %s\n", IS_SRIOV(bp) ? "ON" : "OFF"); - if (!IS_SRIOV(bp)) - return; - - REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0); + if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV)) + REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0); } static int bnx2x_vf_bus(struct bnx2x *bp, int vfid) @@ -2822,6 +2786,18 @@ int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf, dma_addr_t *sb_map) return 0; } +struct set_vf_state_cookie { + struct bnx2x_virtf *vf; + u8 state; +}; + +void bnx2x_set_vf_state(void *cookie) +{ + struct set_vf_state_cookie *p = (struct set_vf_state_cookie *)cookie; + + p->vf->state = p->state; +} + /* VFOP close (teardown the queues, delete mcasts and close HW) */ static void bnx2x_vfop_close(struct bnx2x *bp, struct bnx2x_virtf *vf) { @@ -2872,7 +2848,19 @@ static void bnx2x_vfop_close(struct bnx2x *bp, struct bnx2x_virtf *vf) op_err: BNX2X_ERR("VF[%d] CLOSE error: rc %d\n", vf->abs_vfid, vfop->rc); op_done: - vf->state = VF_ACQUIRED; + + /* need to make sure there are no outstanding stats ramrods which may + * cause the device to access the VF's stats buffer which it will free + * as soon as we return from the close flow. + */ + { + struct set_vf_state_cookie cookie; + + cookie.vf = vf; + cookie.state = VF_ACQUIRED; + bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie); + } + DP(BNX2X_MSG_IOV, "set state to acquired\n"); bnx2x_vfop_end(bp, vf, vfop); } @@ -3084,8 +3072,9 @@ void bnx2x_disable_sriov(struct bnx2x *bp) pci_disable_sriov(bp->pdev); } -static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx, - struct bnx2x_virtf *vf) +static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin) { if (bp->state != BNX2X_STATE_OPEN) { BNX2X_ERR("vf ndo called though PF is down\n"); @@ -3103,12 +3092,22 @@ static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx, return -EINVAL; } - if (!vf) { + /* init members */ + *vf = BP_VF(bp, vfidx); + *bulletin = BP_VF_BULLETIN(bp, vfidx); + + if (!*vf) { BNX2X_ERR("vf ndo called but vf was null. vfidx was %d\n", vfidx); return -EINVAL; } + if (!*bulletin) { + BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n", + vfidx); + return -EINVAL; + } + return 0; } @@ -3116,17 +3115,19 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, struct ifla_vf_info *ivi) { struct bnx2x *bp = netdev_priv(dev); - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj); - struct bnx2x_vlan_mac_obj *vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; + struct bnx2x_vlan_mac_obj *mac_obj; + struct bnx2x_vlan_mac_obj *vlan_obj; int rc; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; - if (!mac_obj || !vlan_obj || !bulletin) { + mac_obj = &bnx2x_vfq(vf, 0, mac_obj); + vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj); + if (!mac_obj || !vlan_obj) { BNX2X_ERR("VF partially initialized\n"); return -EINVAL; } @@ -3183,11 +3184,11 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) { struct bnx2x *bp = netdev_priv(dev); int rc, q_logical_state; - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; if (!is_valid_ether_addr(mac)) { @@ -3249,11 +3250,11 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) { struct bnx2x *bp = netdev_priv(dev); int rc, q_logical_state; - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index d63d132..86436c7 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -522,20 +522,16 @@ static void bnx2x_func_stats_init(struct bnx2x *bp) /* should be called under stats_sema */ static void __bnx2x_stats_start(struct bnx2x *bp) { - /* vfs travel through here as part of the statistics FSM, but no action - * is required - */ - if (IS_VF(bp)) - return; - - if (bp->port.pmf) - bnx2x_port_stats_init(bp); + if (IS_PF(bp)) { + if (bp->port.pmf) + bnx2x_port_stats_init(bp); - else if (bp->func_stx) - bnx2x_func_stats_init(bp); + else if (bp->func_stx) + bnx2x_func_stats_init(bp); - bnx2x_hw_stats_post(bp); - bnx2x_storm_stats_post(bp); + bnx2x_hw_stats_post(bp); + bnx2x_storm_stats_post(bp); + } bp->stats_started = true; } @@ -1997,3 +1993,14 @@ void bnx2x_afex_collect_stats(struct bnx2x *bp, void *void_afex_stats, estats->mac_discard); } } + +void bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie){ + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); + bnx2x_stats_comp(bp); + func_to_exec(cookie); + __bnx2x_stats_start(bp); + up(&bp->stats_sema); +} diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index 853824d..f358450 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -539,6 +539,9 @@ struct bnx2x; void bnx2x_memset_stats(struct bnx2x *bp); void bnx2x_stats_init(struct bnx2x *bp); void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event); +void bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie); /** * bnx2x_save_statistics - save statistics when unloading. diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 181edb5..3d91a5e 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2563,8 +2563,8 @@ static int be_close(struct net_device *netdev) /* Wait for all pending tx completions to arrive so that * all tx skbs are freed. */ - be_tx_compl_clean(adapter); netif_tx_disable(netdev); + be_tx_compl_clean(adapter); be_rx_qs_destroy(adapter); @@ -4373,6 +4373,10 @@ static int be_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); + status = be_fw_wait_ready(adapter); + if (status) + return status; + /* tell fw we're ready to fire cmds */ status = be_cmd_fw_init(adapter); if (status) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 77ea0db..c610a27 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -971,8 +971,7 @@ fec_enet_rx(struct net_device *ndev, int budget) htons(ETH_P_8021Q), vlan_tag); - if (!skb_defer_rx_timestamp(skb)) - napi_gro_receive(&fep->napi, skb); + napi_gro_receive(&fep->napi, skb); } bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data, diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 7fbe6ab..23de82a 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -3069,7 +3069,7 @@ jme_init_one(struct pci_dev *pdev, jwrite32(jme, JME_APMC, apmc); } - NETIF_NAPI_SET(netdev, &jme->napi, jme_poll, jme->rx_ring_size >> 2) + NETIF_NAPI_SET(netdev, &jme->napi, jme_poll, NAPI_POLL_WEIGHT) spin_lock_init(&jme->phy_lock); spin_lock_init(&jme->macaddr_lock); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 3fe09ab..32675e1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -1171,7 +1171,6 @@ typedef struct { #define NETXEN_DB_MAPSIZE_BYTES 0x1000 -#define NETXEN_NETDEV_WEIGHT 128 #define NETXEN_ADAPTER_UP_MAGIC 777 #define NETXEN_NIC_PEG_TUNE 0 diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index c401b0b..ec4cf7f 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -197,7 +197,7 @@ netxen_napi_add(struct netxen_adapter *adapter, struct net_device *netdev) for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; netif_napi_add(netdev, &sds_ring->napi, - netxen_nic_poll, NETXEN_NETDEV_WEIGHT); + netxen_nic_poll, NAPI_POLL_WEIGHT); } return 0; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index b5eb419..85e5c97 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7088,7 +7088,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) RTL_W8(Cfg9346, Cfg9346_Unlock); RTL_W8(Config1, RTL_R8(Config1) | PMEnable); - RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + RTL_W8(Config5, RTL_R8(Config5) & (BWF | MWF | UWF | LanWake | PMEStatus)); if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0) tp->features |= RTL_FEATURE_WOL; if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0) diff --git a/drivers/net/ethernet/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c index 2a469b2..30d7442 100644 --- a/drivers/net/ethernet/sfc/filter.c +++ b/drivers/net/ethernet/sfc/filter.c @@ -675,7 +675,7 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0); BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF != EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF); - rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF; + rep_index = spec->type - EFX_FILTER_UC_DEF; ins_index = rep_index; spin_lock_bh(&state->lock); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 03de76c..1c83a44 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -71,14 +71,18 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, plat->force_sf_dma_mode = 1; } - dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL); - if (!dma_cfg) - return -ENOMEM; - - plat->dma_cfg = dma_cfg; - of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); - dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); - dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + if (of_find_property(np, "snps,pbl", NULL)) { + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), + GFP_KERNEL); + if (!dma_cfg) + return -ENOMEM; + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + dma_cfg->fixed_burst = + of_property_read_bool(np, "snps,fixed-burst"); + dma_cfg->mixed_burst = + of_property_read_bool(np, "snps,mixed-burst"); + } return 0; } diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index ad32af6..9c805e0 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1466,8 +1466,7 @@ static void gelic_ether_setup_netdev_ops(struct net_device *netdev, { netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; /* NAPI */ - netif_napi_add(netdev, napi, - gelic_net_poll, GELIC_NET_NAPI_WEIGHT); + netif_napi_add(netdev, napi, gelic_net_poll, NAPI_POLL_WEIGHT); netdev->ethtool_ops = &gelic_ether_ethtool_ops; netdev->netdev_ops = &gelic_netdevice_ops; } diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h index a93df6a..309abb4 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.h +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h @@ -37,7 +37,6 @@ #define GELIC_NET_RXBUF_ALIGN 128 #define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */ #define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ -#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS) #define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL #define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */ diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index e90e1f4..64b4639 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -175,6 +175,7 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) printk(KERN_WARNING "Setting MDIO clock divisor to " "default %d\n", DEFAULT_CLOCK_DIVISOR); clk_div = DEFAULT_CLOCK_DIVISOR; + of_node_put(np1); goto issue; } diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 51f2bc3..2dcc60f 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -210,8 +210,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); pci_write_config_byte(pcidev,0x5a,0xc0); WriteLPCReg(0x28, 0x70 ); - if (via_ircc_open(pcidev, &info, 0x3076) == 0) - rc=0; + rc = via_ircc_open(pcidev, &info, 0x3076); } else rc = -ENODEV; //IR not turn on } else { //Not VT1211 @@ -249,8 +248,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) info.irq=FirIRQ; info.dma=FirDRQ1; info.dma2=FirDRQ0; - if (via_ircc_open(pcidev, &info, 0x3096) == 0) - rc=0; + rc = via_ircc_open(pcidev, &info, 0x3096); } else rc = -ENODEV; //IR not turn on !!!!! }//Not VT1211 diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index b51db2a..ea53abb 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -68,6 +68,8 @@ static const struct proto_ops macvtap_socket_ops; #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ NETIF_F_TSO6 | NETIF_F_UFO) #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) +#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG) + /* * RCU usage: * The macvtap_queue and the macvlan_dev are loosely coupled, the @@ -278,7 +280,8 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) { struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *q = macvtap_get_queue(dev, skb); - netdev_features_t features; + netdev_features_t features = TAP_FEATURES; + if (!q) goto drop; @@ -287,9 +290,11 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) skb->dev = dev; /* Apply the forward feature mask so that we perform segmentation - * according to users wishes. + * according to users wishes. This only works if VNET_HDR is + * enabled. */ - features = netif_skb_features(skb) & vlan->tap_features; + if (q->flags & IFF_VNET_HDR) + features |= vlan->tap_features; if (netif_needs_gso(skb, features)) { struct sk_buff *segs = __skb_gso_segment(skb, features, false); @@ -1064,8 +1069,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg) /* tap_features are the same as features on tun/tap and * reflect user expectations. */ - vlan->tap_features = vlan->dev->features & - (feature_mask | ~TUN_OFFLOADS); + vlan->tap_features = feature_mask; vlan->set_features = features; netdev_update_features(vlan->dev); @@ -1161,10 +1165,6 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, TUN_F_TSO_ECN | TUN_F_UFO)) return -EINVAL; - /* TODO: only accept frames with the features that - got enabled for forwarded frames */ - if (!(q->flags & IFF_VNET_HDR)) - return -EINVAL; rtnl_lock(); ret = set_offload(q, arg); rtnl_unlock(); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 8e7af83..138de83 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -23,7 +23,7 @@ #define RTL821x_INER_INIT 0x6400 #define RTL821x_INSR 0x13 -#define RTL8211E_INER_LINK_STAT 0x10 +#define RTL8211E_INER_LINK_STATUS 0x400 MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); @@ -57,7 +57,7 @@ static int rtl8211e_config_intr(struct phy_device *phydev) if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, RTL821x_INER, - RTL8211E_INER_LINK_STAT); + RTL8211E_INER_LINK_STATUS); else err = phy_write(phydev, RTL821x_INER, 0); diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 8728198..25ba7ec 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -400,6 +400,10 @@ static const struct usb_device_id mbim_devs[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info_zlp, }, + /* HP hs2434 Mobile Broadband Module needs ZLPs */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info_zlp, + }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index cba1d46..86292e6 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2816,13 +2816,16 @@ exit: static int hso_get_config_data(struct usb_interface *interface) { struct usb_device *usbdev = interface_to_usbdev(interface); - u8 config_data[17]; + u8 *config_data = kmalloc(17, GFP_KERNEL); u32 if_num = interface->altsetting->desc.bInterfaceNumber; s32 result; + if (!config_data) + return -ENOMEM; if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x86, 0xC0, 0, 0, config_data, 17, USB_CTRL_SET_TIMEOUT) != 0x11) { + kfree(config_data); return -EIO; } @@ -2873,6 +2876,7 @@ static int hso_get_config_data(struct usb_interface *interface) if (config_data[16] & 0x1) result |= HSO_INFO_CRC_BUG; + kfree(config_data); return result; } @@ -2886,6 +2890,11 @@ static int hso_probe(struct usb_interface *interface, struct hso_shared_int *shared_int; struct hso_device *tmp_dev = NULL; + if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { + dev_err(&interface->dev, "Not our interface\n"); + return -ENODEV; + } + if_num = interface->altsetting->desc.bInterfaceNumber; /* Get the interface/port specification from either driver_info or from @@ -2895,10 +2904,6 @@ static int hso_probe(struct usb_interface *interface, else port_spec = hso_get_config_data(interface); - if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { - dev_err(&interface->dev, "Not our interface\n"); - return -ENODEV; - } /* Check if we need to switch to alt interfaces prior to port * configuration */ if (interface->num_altsetting > 1) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index e602c95..c028df7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -448,6 +448,7 @@ static void ath9k_htc_tx_process(struct ath9k_htc_priv *priv, struct ieee80211_conf *cur_conf = &priv->hw->conf; bool txok; int slot; + int hdrlen, padsize; slot = strip_drv_header(priv, skb); if (slot < 0) { @@ -504,6 +505,15 @@ send_mac80211: ath9k_htc_tx_clear_slot(priv, slot); + /* Remove padding before handing frame back to mac80211 */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + padsize = hdrlen & 3; + if (padsize && skb->len > hdrlen + padsize) { + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); + } + /* Send status to mac80211 */ ieee80211_tx_status(priv->hw, skb); } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 16f8b20..026a2a0 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -802,7 +802,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_SUPPORTS_RC_TABLE; + IEEE80211_HW_SUPPORTS_RC_TABLE | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1737a3e..cb5a655 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -173,8 +173,7 @@ static void ath_restart_work(struct ath_softc *sc) { ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9485(sc->sc_ah) || - AR_SREV_9550(sc->sc_ah)) + if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah)) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 4a33c6e..349fa22 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1860,7 +1860,8 @@ void *carl9170_alloc(size_t priv_size) IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SUPPORTS_RC_TABLE | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; if (!modparam_noht) { /* diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index ac07473..e509030 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -523,9 +523,9 @@ static int prism2_ioctl_giwaplist(struct net_device *dev, data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); - memcpy(extra, &addr, sizeof(struct sockaddr) * data->length); + memcpy(extra, addr, sizeof(struct sockaddr) * data->length); data->flags = 1; /* has quality information */ - memcpy(extra + sizeof(struct sockaddr) * data->length, &qual, + memcpy(extra + sizeof(struct sockaddr) * data->length, qual, sizeof(struct iw_quality) * data->length); kfree(addr); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index f2ed62e..7acf5ee 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -4464,9 +4464,9 @@ il4965_irq_tasklet(struct il_priv *il) set_bit(S_RFKILL, &il->status); } else { clear_bit(S_RFKILL, &il->status); - wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); il_force_reset(il, true); } + wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); handled |= CSR_INT_BIT_RF_KILL; } diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 822f1a0..3193872 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1068,7 +1068,10 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + return; + + if (ctx->vif) ieee80211_chswitch_done(ctx->vif, is_success); } diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index a70c7b9..ff8cc75 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -97,8 +97,6 @@ #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) -#define APMG_RTC_INT_STT_RFKILL (0x10000000) - /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index ad9bbca..7fd6fbf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -138,6 +138,20 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) schedule_work(&mvm->roc_done_wk); } +static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const char *errmsg) +{ + if (vif->type != NL80211_IFTYPE_STATION) + return false; + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) + return false; + if (errmsg) + IWL_ERR(mvm, "%s\n", errmsg); + ieee80211_connection_loss(vif); + return true; +} + /* * Handles a FW notification for an event that is known to the driver. * @@ -163,8 +177,13 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * P2P Device discoveribility, while there are other higher priority * events in the system). */ - WARN_ONCE(!le32_to_cpu(notif->status), - "Failed to schedule time event\n"); + if (WARN_ONCE(!le32_to_cpu(notif->status), + "Failed to schedule time event\n")) { + if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) { + iwl_mvm_te_clear_data(mvm, te_data); + return; + } + } if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) { IWL_DEBUG_TE(mvm, @@ -180,14 +199,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * By now, we should have finished association * and know the dtim period. */ - if (te_data->vif->type == NL80211_IFTYPE_STATION && - (!te_data->vif->bss_conf.assoc || - !te_data->vif->bss_conf.dtim_period)) { - IWL_ERR(mvm, - "No assocation and the time event is over already...\n"); - ieee80211_connection_loss(te_data->vif); - } - + iwl_mvm_te_check_disconnect(mvm, te_data->vif, + "No assocation and the time event is over already..."); iwl_mvm_te_clear_data(mvm, te_data); } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) { te_data->running = true; diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index f600e68..fd848cd 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -888,14 +888,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (hw_rfkill) { - /* - * Clear the interrupt in APMG if the NIC is going down. - * Note that when the NIC exits RFkill (else branch), we - * can't access prph and the NIC will be reset in - * start_hw anyway. - */ - iwl_write_prph(trans, APMG_RTC_INT_STT_REG, - APMG_RTC_INT_STT_RFKILL); set_bit(STATUS_RFKILL, &trans_pcie->status); if (test_and_clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 96cfcdd..390e2f0 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1502,16 +1502,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->reg_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); - /* W/A - seems to solve weird behavior. We need to remove this if we - * don't want to stay in L1 all the time. This wastes a lot of power */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - if (pci_enable_device(pdev)) { err = -ENODEV; goto out_no_pci; } + /* W/A - seems to solve weird behavior. We need to remove this if we + * don't want to stay in L1 all the time. This wastes a lot of power */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 1f80ea5..1b41c8e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -6133,7 +6133,8 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; /* * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 4941f20..b8ba1f9 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -98,10 +98,12 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw) goto exit; err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, - USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT); + USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT); if (err < 0) goto exit; + memcpy(&ret, buf, sizeof(ret)); + if (ret & 0x80) { err = -EIO; goto exit; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 6bb7cf2..b10ba00 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -392,6 +392,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, mem = (unsigned long) dt_alloc(size + 4, __alignof__(struct device_node)); + memset((void *)mem, 0, size); + ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); pr_debug(" unflattening %lx...\n", mem); diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index c47fd1e..94716c7 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -278,6 +278,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl_group *g = &pctl->groups[group]; + unsigned long flags; u32 val, mask; u16 strength; u8 dlevel; @@ -295,22 +296,35 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, * 3: 40mA */ dlevel = strength / 10 - 1; + + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), pctl->membase + sunxi_dlevel_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; case PIN_CONFIG_BIAS_PULL_UP: + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), pctl->membase + sunxi_pull_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; case PIN_CONFIG_BIAS_PULL_DOWN: + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), pctl->membase + sunxi_pull_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; default: break; @@ -360,11 +374,17 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev, u8 config) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned long flags; + u32 val, mask; + + spin_lock_irqsave(&pctl->lock, flags); - u32 val = readl(pctl->membase + sunxi_mux_reg(pin)); - u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin); + val = readl(pctl->membase + sunxi_mux_reg(pin)); + mask = MUX_PINS_MASK << sunxi_mux_offset(pin); writel((val & ~mask) | config << sunxi_mux_offset(pin), pctl->membase + sunxi_mux_reg(pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); } static int sunxi_pmx_enable(struct pinctrl_dev *pctldev, @@ -464,8 +484,21 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); u32 reg = sunxi_data_reg(offset); u8 index = sunxi_data_offset(offset); + unsigned long flags; + u32 regval; + + spin_lock_irqsave(&pctl->lock, flags); + + regval = readl(pctl->membase + reg); - writel((value & DATA_PINS_MASK) << index, pctl->membase + reg); + if (value) + regval |= BIT(index); + else + regval &= ~(BIT(index)); + + writel(regval, pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, @@ -526,6 +559,8 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); u32 reg = sunxi_irq_cfg_reg(d->hwirq); u8 index = sunxi_irq_cfg_offset(d->hwirq); + unsigned long flags; + u32 regval; u8 mode; switch (type) { @@ -548,7 +583,13 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, return -EINVAL; } - writel((mode & IRQ_CFG_IRQ_MASK) << index, pctl->membase + reg); + spin_lock_irqsave(&pctl->lock, flags); + + regval = readl(pctl->membase + reg); + regval &= ~IRQ_CFG_IRQ_MASK; + writel(regval | (mode << index), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); return 0; } @@ -560,14 +601,19 @@ static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d) u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq); u32 status_reg = sunxi_irq_status_reg(d->hwirq); u8 status_idx = sunxi_irq_status_offset(d->hwirq); + unsigned long flags; u32 val; + spin_lock_irqsave(&pctl->lock, flags); + /* Mask the IRQ */ val = readl(pctl->membase + ctrl_reg); writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg); /* Clear the IRQ */ writel(1 << status_idx, pctl->membase + status_reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static void sunxi_pinctrl_irq_mask(struct irq_data *d) @@ -575,11 +621,16 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + unsigned long flags; u32 val; + spin_lock_irqsave(&pctl->lock, flags); + /* Mask the IRQ */ val = readl(pctl->membase + reg); writel(val & ~(1 << idx), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static void sunxi_pinctrl_irq_unmask(struct irq_data *d) @@ -588,6 +639,7 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) struct sunxi_desc_function *func; u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + unsigned long flags; u32 val; func = sunxi_pinctrl_desc_find_function_by_pin(pctl, @@ -597,9 +649,13 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) /* Change muxing to INT mode */ sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); + spin_lock_irqsave(&pctl->lock, flags); + /* Unmask the IRQ */ val = readl(pctl->membase + reg); writel(val | (1 << idx), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static struct irq_chip sunxi_pinctrl_irq_chip = { @@ -752,6 +808,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, pctl); + spin_lock_init(&pctl->lock); + pctl->membase = of_iomap(node, 0); if (!pctl->membase) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-sunxi.h b/drivers/pinctrl/pinctrl-sunxi.h index d68047d..01c494f 100644 --- a/drivers/pinctrl/pinctrl-sunxi.h +++ b/drivers/pinctrl/pinctrl-sunxi.h @@ -14,6 +14,7 @@ #define __PINCTRL_SUNXI_H #include <linux/kernel.h> +#include <linux/spinlock.h> #define PA_BASE 0 #define PB_BASE 32 @@ -407,6 +408,7 @@ struct sunxi_pinctrl { unsigned ngroups; int irq; int irq_array[SUNXI_IRQ_NUMBER]; + spinlock_t lock; struct pinctrl_dev *pctl_dev; }; diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 0f9f859..f911952 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -330,7 +330,7 @@ static int __init olpc_ec_init_module(void) return platform_driver_register(&olpc_ec_plat_driver); } -module_init(olpc_ec_init_module); +arch_initcall(olpc_ec_init_module); MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 97bb05e..d6970f4 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,7 +53,6 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_ALS_QUERY 0x3 #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 -#define HPWMI_BIOS_QUERY 0x9 #define HPWMI_HOTKEY_QUERY 0xc #define HPWMI_WIRELESS2_QUERY 0x1b #define HPWMI_POSTCODEERROR_QUERY 0x2a @@ -293,19 +292,6 @@ static int hp_wmi_tablet_state(void) return (state & 0x4) ? 1 : 0; } -static int hp_wmi_enable_hotkeys(void) -{ - int ret; - int query = 0x6e; - - ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), - 0); - - if (ret) - return -EINVAL; - return 0; -} - static int hp_wmi_set_block(void *data, bool blocked) { enum hp_wmi_radio r = (enum hp_wmi_radio) data; @@ -1009,8 +995,6 @@ static int __init hp_wmi_init(void) err = hp_wmi_input_setup(); if (err) return err; - - hp_wmi_enable_hotkeys(); } if (bios_capable) { diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2ac045f..3a1b6bf 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2440,7 +2440,10 @@ static ssize_t sony_nc_gfx_switch_status_show(struct device *dev, if (pos < 0) return pos; - return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina"); + return snprintf(buffer, PAGE_SIZE, "%s\n", + pos == SPEED ? "speed" : + pos == STAMINA ? "stamina" : + pos == AUTO ? "auto" : "unknown"); } static int sony_nc_gfx_switch_setup(struct platform_device *pd, @@ -4320,7 +4323,8 @@ static int sony_pic_add(struct acpi_device *device) goto err_free_resources; } - if (sonypi_compat_init()) + result = sonypi_compat_init(); + if (result) goto err_remove_input; /* request io port */ diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c new file mode 100644 index 0000000..3459f60 --- /dev/null +++ b/drivers/regulator/88pm800.c @@ -0,0 +1,383 @@ +/* + * Regulators driver for Marvell 88PM800 + * + * Copyright (C) 2012 Marvell International Ltd. + * Joseph(Yossi) Hanin <yhanin@marvell.com> + * Yi Zhang <yizhang@marvell.com> + * + * 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/moduleparam.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/88pm80x.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/regulator/of_regulator.h> + +/* LDO1 with DVC[0..3] */ +#define PM800_LDO1_VOUT (0x08) /* VOUT1 */ +#define PM800_LDO1_VOUT_2 (0x09) +#define PM800_LDO1_VOUT_3 (0x0A) +#define PM800_LDO2_VOUT (0x0B) +#define PM800_LDO3_VOUT (0x0C) +#define PM800_LDO4_VOUT (0x0D) +#define PM800_LDO5_VOUT (0x0E) +#define PM800_LDO6_VOUT (0x0F) +#define PM800_LDO7_VOUT (0x10) +#define PM800_LDO8_VOUT (0x11) +#define PM800_LDO9_VOUT (0x12) +#define PM800_LDO10_VOUT (0x13) +#define PM800_LDO11_VOUT (0x14) +#define PM800_LDO12_VOUT (0x15) +#define PM800_LDO13_VOUT (0x16) +#define PM800_LDO14_VOUT (0x17) +#define PM800_LDO15_VOUT (0x18) +#define PM800_LDO16_VOUT (0x19) +#define PM800_LDO17_VOUT (0x1A) +#define PM800_LDO18_VOUT (0x1B) +#define PM800_LDO19_VOUT (0x1C) + +/* BUCK1 with DVC[0..3] */ +#define PM800_BUCK1 (0x3C) +#define PM800_BUCK1_1 (0x3D) +#define PM800_BUCK1_2 (0x3E) +#define PM800_BUCK1_3 (0x3F) +#define PM800_BUCK2 (0x40) +#define PM800_BUCK3 (0x41) +#define PM800_BUCK3 (0x41) +#define PM800_BUCK4 (0x42) +#define PM800_BUCK4_1 (0x43) +#define PM800_BUCK4_2 (0x44) +#define PM800_BUCK4_3 (0x45) +#define PM800_BUCK5 (0x46) + +#define PM800_BUCK_ENA (0x50) +#define PM800_LDO_ENA1_1 (0x51) +#define PM800_LDO_ENA1_2 (0x52) +#define PM800_LDO_ENA1_3 (0x53) + +#define PM800_LDO_ENA2_1 (0x56) +#define PM800_LDO_ENA2_2 (0x57) +#define PM800_LDO_ENA2_3 (0x58) + +#define PM800_BUCK1_MISC1 (0x78) +#define PM800_BUCK3_MISC1 (0x7E) +#define PM800_BUCK4_MISC1 (0x81) +#define PM800_BUCK5_MISC1 (0x84) + +struct pm800_regulator_info { + struct regulator_desc desc; + int max_ua; +}; + +struct pm800_regulators { + struct regulator_dev *regulators[PM800_ID_RG_MAX]; + struct pm80x_chip *chip; + struct regmap *map; +}; + +/* + * vreg - the buck regs string. + * ereg - the string for the enable register. + * ebit - the bit number in the enable register. + * amax - the current + * Buck has 2 kinds of voltage steps. It is easy to find voltage by ranges, + * not the constant voltage table. + * n_volt - Number of available selectors + */ +#define PM800_BUCK(vreg, ereg, ebit, amax, volt_ranges, n_volt) \ +{ \ + .desc = { \ + .name = #vreg, \ + .ops = &pm800_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM800_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = n_volt, \ + .linear_ranges = volt_ranges, \ + .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ + .vsel_reg = PM800_##vreg, \ + .vsel_mask = 0x7f, \ + .enable_reg = PM800_##ereg, \ + .enable_mask = 1 << (ebit), \ + }, \ + .max_ua = (amax), \ +} + +/* + * vreg - the LDO regs string + * ereg - the string for the enable register. + * ebit - the bit number in the enable register. + * amax - the current + * volt_table - the LDO voltage table + * For all the LDOes, there are too many ranges. Using volt_table will be + * simpler and faster. + */ +#define PM800_LDO(vreg, ereg, ebit, amax, ldo_volt_table) \ +{ \ + .desc = { \ + .name = #vreg, \ + .ops = &pm800_volt_table_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM800_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(ldo_volt_table), \ + .vsel_reg = PM800_##vreg##_VOUT, \ + .vsel_mask = 0x1f, \ + .enable_reg = PM800_##ereg, \ + .enable_mask = 1 << (ebit), \ + .volt_table = ldo_volt_table, \ + }, \ + .max_ua = (amax), \ +} + +/* Ranges are sorted in ascending order. */ +static const struct regulator_linear_range buck1_volt_range[] = { + { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f, + .uV_step = 12500 }, + { .min_uV = 1600000, .max_uV = 1800000, .min_sel = 0x50, + .max_sel = 0x54, .uV_step = 50000 }, +}; + +/* BUCK 2~5 have same ranges. */ +static const struct regulator_linear_range buck2_5_volt_range[] = { + { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f, + .uV_step = 12500 }, + { .min_uV = 1600000, .max_uV = 3300000, .min_sel = 0x50, + .max_sel = 0x72, .uV_step = 50000 }, +}; + +static const unsigned int ldo1_volt_table[] = { + 600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000, + 1000000, 1050000, 1100000, 1150000, 1200000, 1300000, 1400000, 1500000, +}; + +static const unsigned int ldo2_volt_table[] = { + 1700000, 1800000, 1900000, 2000000, 2100000, 2500000, 2700000, 2800000, +}; + +/* LDO 3~17 have same voltage table. */ +static const unsigned int ldo3_17_volt_table[] = { + 1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000, + 2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, +}; + +/* LDO 18~19 have same voltage table. */ +static const unsigned int ldo18_19_volt_table[] = { + 1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000, +}; + +static int pm800_get_current_limit(struct regulator_dev *rdev) +{ + struct pm800_regulator_info *info = rdev_get_drvdata(rdev); + + return info->max_ua; +} + +static struct regulator_ops pm800_volt_range_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_current_limit = pm800_get_current_limit, +}; + +static struct regulator_ops pm800_volt_table_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_current_limit = pm800_get_current_limit, +}; + +/* The array is indexed by id(PM800_ID_XXX) */ +static struct pm800_regulator_info pm800_regulator_info[] = { + PM800_BUCK(BUCK1, BUCK_ENA, 0, 3000000, buck1_volt_range, 0x55), + PM800_BUCK(BUCK2, BUCK_ENA, 1, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(BUCK3, BUCK_ENA, 2, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(BUCK4, BUCK_ENA, 3, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(BUCK5, BUCK_ENA, 4, 1200000, buck2_5_volt_range, 0x73), + + PM800_LDO(LDO1, LDO_ENA1_1, 0, 200000, ldo1_volt_table), + PM800_LDO(LDO2, LDO_ENA1_1, 1, 10000, ldo2_volt_table), + PM800_LDO(LDO3, LDO_ENA1_1, 2, 300000, ldo3_17_volt_table), + PM800_LDO(LDO4, LDO_ENA1_1, 3, 300000, ldo3_17_volt_table), + PM800_LDO(LDO5, LDO_ENA1_1, 4, 300000, ldo3_17_volt_table), + PM800_LDO(LDO6, LDO_ENA1_1, 5, 300000, ldo3_17_volt_table), + PM800_LDO(LDO7, LDO_ENA1_1, 6, 300000, ldo3_17_volt_table), + PM800_LDO(LDO8, LDO_ENA1_1, 7, 300000, ldo3_17_volt_table), + PM800_LDO(LDO9, LDO_ENA1_2, 0, 300000, ldo3_17_volt_table), + PM800_LDO(LDO10, LDO_ENA1_2, 1, 300000, ldo3_17_volt_table), + PM800_LDO(LDO11, LDO_ENA1_2, 2, 300000, ldo3_17_volt_table), + PM800_LDO(LDO12, LDO_ENA1_2, 3, 300000, ldo3_17_volt_table), + PM800_LDO(LDO13, LDO_ENA1_2, 4, 300000, ldo3_17_volt_table), + PM800_LDO(LDO14, LDO_ENA1_2, 5, 300000, ldo3_17_volt_table), + PM800_LDO(LDO15, LDO_ENA1_2, 6, 300000, ldo3_17_volt_table), + PM800_LDO(LDO16, LDO_ENA1_2, 7, 300000, ldo3_17_volt_table), + PM800_LDO(LDO17, LDO_ENA1_3, 0, 300000, ldo3_17_volt_table), + PM800_LDO(LDO18, LDO_ENA1_3, 1, 200000, ldo18_19_volt_table), + PM800_LDO(LDO19, LDO_ENA1_3, 2, 200000, ldo18_19_volt_table), +}; + +#define PM800_REGULATOR_OF_MATCH(_name, _id) \ + [PM800_ID_##_id] = { \ + .name = #_name, \ + .driver_data = &pm800_regulator_info[PM800_ID_##_id], \ + } + +static struct of_regulator_match pm800_regulator_matches[] = { + PM800_REGULATOR_OF_MATCH(buck1, BUCK1), + PM800_REGULATOR_OF_MATCH(buck2, BUCK2), + PM800_REGULATOR_OF_MATCH(buck3, BUCK3), + PM800_REGULATOR_OF_MATCH(buck4, BUCK4), + PM800_REGULATOR_OF_MATCH(buck5, BUCK5), + PM800_REGULATOR_OF_MATCH(ldo1, LDO1), + PM800_REGULATOR_OF_MATCH(ldo2, LDO2), + PM800_REGULATOR_OF_MATCH(ldo3, LDO3), + PM800_REGULATOR_OF_MATCH(ldo4, LDO4), + PM800_REGULATOR_OF_MATCH(ldo5, LDO5), + PM800_REGULATOR_OF_MATCH(ldo6, LDO6), + PM800_REGULATOR_OF_MATCH(ldo7, LDO7), + PM800_REGULATOR_OF_MATCH(ldo8, LDO8), + PM800_REGULATOR_OF_MATCH(ldo9, LDO9), + PM800_REGULATOR_OF_MATCH(ldo10, LDO10), + PM800_REGULATOR_OF_MATCH(ldo11, LDO11), + PM800_REGULATOR_OF_MATCH(ldo12, LDO12), + PM800_REGULATOR_OF_MATCH(ldo13, LDO13), + PM800_REGULATOR_OF_MATCH(ldo14, LDO14), + PM800_REGULATOR_OF_MATCH(ldo15, LDO15), + PM800_REGULATOR_OF_MATCH(ldo16, LDO16), + PM800_REGULATOR_OF_MATCH(ldo17, LDO17), + PM800_REGULATOR_OF_MATCH(ldo18, LDO18), + PM800_REGULATOR_OF_MATCH(ldo19, LDO19), +}; + +static int pm800_regulator_dt_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + ret = of_regulator_match(&pdev->dev, np, + pm800_regulator_matches, + ARRAY_SIZE(pm800_regulator_matches)); + if (ret < 0) + return ret; + + return 0; +} + +static int pm800_regulator_probe(struct platform_device *pdev) +{ + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm80x_platform_data *pdata = dev_get_platdata(pdev->dev.parent); + struct pm800_regulators *pm800_data; + struct pm800_regulator_info *info; + struct regulator_config config = { }; + struct regulator_init_data *init_data; + int i, ret; + + if (!pdata || pdata->num_regulators == 0) { + if (IS_ENABLED(CONFIG_OF)) { + ret = pm800_regulator_dt_init(pdev); + if (ret) + return ret; + } else { + return -ENODEV; + } + } else if (pdata->num_regulators) { + unsigned int count = 0; + + /* Check whether num_regulator is valid. */ + for (i = 0; i < ARRAY_SIZE(pdata->regulators); i++) { + if (pdata->regulators[i]) + count++; + } + if (count != pdata->num_regulators) + return -EINVAL; + } else { + return -EINVAL; + } + + pm800_data = devm_kzalloc(&pdev->dev, sizeof(*pm800_data), + GFP_KERNEL); + if (!pm800_data) { + dev_err(&pdev->dev, "Failed to allocate pm800_regualtors"); + return -ENOMEM; + } + + pm800_data->map = chip->subchip->regmap_power; + pm800_data->chip = chip; + + platform_set_drvdata(pdev, pm800_data); + + for (i = 0; i < PM800_ID_RG_MAX; i++) { + if (!pdata || pdata->num_regulators == 0) + init_data = pm800_regulator_matches[i].init_data; + else + init_data = pdata->regulators[i]; + if (!init_data) + continue; + info = pm800_regulator_matches[i].driver_data; + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = info; + config.regmap = pm800_data->map; + config.of_node = pm800_regulator_matches[i].of_node; + + pm800_data->regulators[i] = + regulator_register(&info->desc, &config); + if (IS_ERR(pm800_data->regulators[i])) { + ret = PTR_ERR(pm800_data->regulators[i]); + dev_err(&pdev->dev, "Failed to register %s\n", + info->desc.name); + + while (--i >= 0) + regulator_unregister(pm800_data->regulators[i]); + + return ret; + } + } + + return 0; +} + +static int pm800_regulator_remove(struct platform_device *pdev) +{ + struct pm800_regulators *pm800_data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < PM800_ID_RG_MAX; i++) + regulator_unregister(pm800_data->regulators[i]); + + return 0; +} + +static struct platform_driver pm800_regulator_driver = { + .driver = { + .name = "88pm80x-regulator", + .owner = THIS_MODULE, + }, + .probe = pm800_regulator_probe, + .remove = pm800_regulator_remove, +}; + +module_platform_driver(pm800_regulator_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joseph(Yossi) Hanin <yhanin@marvell.com>"); +MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM800 PMIC"); +MODULE_ALIAS("platform:88pm800-regulator"); diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 8a7cb1f..7023097 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -346,7 +346,7 @@ static int pm8607_regulator_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm8607_regulator_info *info = NULL; - struct regulator_init_data *pdata = pdev->dev.platform_data; + struct regulator_init_data *pdata = dev_get_platdata(&pdev->dev); struct regulator_config config = { }; struct resource *res; int i; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index f1e6ad9..dfe5809 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -64,15 +64,21 @@ config REGULATOR_USERSPACE_CONSUMER If unsure, say no. -config REGULATOR_GPIO - tristate "GPIO regulator support" - depends on GPIOLIB +config REGULATOR_88PM800 + tristate "Marvell 88PM800 Power regulators" + depends on MFD_88PM800 help - This driver provides support for regulators that can be - controlled via gpios. - It is capable of supporting current and voltage regulators - and the platform has to provide a mapping of GPIO-states - to target volts/amps. + This driver supports Marvell 88PM800 voltage regulator chips. + It delivers digitally programmable output, + the voltage is programmed via I2C interface. + It's suitable to support PXA988 chips to control VCC_MAIN and + various voltages. + +config REGULATOR_88PM8607 + tristate "Marvell 88PM8607 Power regulators" + depends on MFD_88PM860X=y + help + This driver supports 88PM8607 voltage regulator chips. config REGULATOR_AD5398 tristate "Analog Devices AD5398/AD5821 regulators" @@ -81,6 +87,14 @@ config REGULATOR_AD5398 This driver supports AD5398 and AD5821 current regulator chips. If building into module, its name is ad5398.ko. +config REGULATOR_ANATOP + tristate "Freescale i.MX on-chip ANATOP LDO regulators" + depends on MFD_SYSCON + help + Say y here to support Freescale i.MX on-chip ANATOP LDOs + regulators. It is recommended that this option be + enabled on i.MX6 platform. + config REGULATOR_AAT2870 tristate "AnalogicTech AAT2870 Regulators" depends on MFD_AAT2870_CORE @@ -88,6 +102,22 @@ config REGULATOR_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the regulator driver. +config REGULATOR_AB3100 + tristate "ST-Ericsson AB3100 Regulator functions" + depends on AB3100_CORE + default y if AB3100_CORE + help + These regulators correspond to functionality in the + AB3100 analog baseband dealing with power regulators + for the system. + +config REGULATOR_AB8500 + bool "ST-Ericsson AB8500 Power Regulators" + depends on AB8500_CORE + help + This driver supports the regulators found on the ST-Ericsson mixed + signal AB8500 PMIC + config REGULATOR_ARIZONA tristate "Wolfson Arizona class devices" depends on MFD_ARIZONA @@ -96,6 +126,13 @@ config REGULATOR_ARIZONA Support for the regulators found on Wolfson Arizona class devices. +config REGULATOR_AS3711 + tristate "AS3711 PMIC" + depends on MFD_AS3711 + help + This driver provides support for the voltage regulators on the + AS3711 PMIC + config REGULATOR_DA903X tristate "Dialog Semiconductor DA9030/DA9034 regulators" depends on PMIC_DA903X @@ -120,6 +157,37 @@ config REGULATOR_DA9055 This driver can also be built as a module. If so, the module will be called da9055-regulator. +config REGULATOR_DA9063 + tristate "Dialog Semiconductor DA9063 regulators" + depends on MFD_DA9063 + help + Say y here to support the BUCKs and LDOs regulators found on + DA9063 PMICs. + + This driver can also be built as a module. If so, the module + will be called da9063-regulator. + +config REGULATOR_DA9210 + tristate "Dialog Semiconductor DA9210 regulator" + depends on I2C + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9210. + The DA9210 is a multi-phase synchronous step down + converter 12A DC-DC Buck controlled through an I2C + interface. + +config REGULATOR_DBX500_PRCMU + bool + +config REGULATOR_DB8500_PRCMU + bool "ST-Ericsson DB8500 Voltage Domain Regulators" + depends on MFD_DB8500_PRCMU + select REGULATOR_DBX500_PRCMU + help + This driver supports the voltage domain regulators controlled by the + DB8500 PRCMU + config REGULATOR_FAN53555 tristate "Fairchild FAN53555 Regulator" depends on I2C @@ -131,44 +199,57 @@ config REGULATOR_FAN53555 input voltage supply of 2.5V to 5.5V. The output voltage is programmed through an I2C interface. -config REGULATOR_ANATOP - tristate "Freescale i.MX on-chip ANATOP LDO regulators" - depends on MFD_SYSCON +config REGULATOR_GPIO + tristate "GPIO regulator support" + depends on GPIOLIB help - Say y here to support Freescale i.MX on-chip ANATOP LDOs - regulators. It is recommended that this option be - enabled on i.MX6 platform. + This driver provides support for regulators that can be + controlled via gpios. + It is capable of supporting current and voltage regulators + and the platform has to provide a mapping of GPIO-states + to target volts/amps. -config REGULATOR_MC13XXX_CORE - tristate +config REGULATOR_ISL6271A + tristate "Intersil ISL6271A Power regulator" + depends on I2C + help + This driver supports ISL6271A voltage regulator chip. -config REGULATOR_MC13783 - tristate "Freescale MC13783 regulator driver" - depends on MFD_MC13783 - select REGULATOR_MC13XXX_CORE +config REGULATOR_LP3971 + tristate "National Semiconductors LP3971 PMIC regulator driver" + depends on I2C help - Say y here to support the regulators found on the Freescale MC13783 - PMIC. + Say Y here to support the voltage regulators and convertors + on National Semiconductors LP3971 PMIC -config REGULATOR_MC13892 - tristate "Freescale MC13892 regulator driver" - depends on MFD_MC13XXX - select REGULATOR_MC13XXX_CORE +config REGULATOR_LP3972 + tristate "National Semiconductors LP3972 PMIC regulator driver" + depends on I2C help - Say y here to support the regulators found on the Freescale MC13892 - PMIC. + Say Y here to support the voltage regulators and convertors + on National Semiconductors LP3972 PMIC -config REGULATOR_ISL6271A - tristate "Intersil ISL6271A Power regulator" +config REGULATOR_LP872X + tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators" depends on I2C + select REGMAP_I2C help - This driver supports ISL6271A voltage regulator chip. + This driver supports LP8720/LP8725 PMIC -config REGULATOR_88PM8607 - bool "Marvell 88PM8607 Power regulators" - depends on MFD_88PM860X=y +config REGULATOR_LP8755 + tristate "TI LP8755 High Performance PMU driver" + depends on I2C + select REGMAP_I2C help - This driver supports 88PM8607 voltage regulator chips. + This driver supports LP8755 High Performance PMU driver. This + chip contains six step-down DC/DC converters which can support + 9 mode multiphase configuration. + +config REGULATOR_LP8788 + tristate "TI LP8788 Power Regulators" + depends on MFD_LP8788 + help + This driver supports LP8788 voltage regulator chip. config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" @@ -259,48 +340,43 @@ config REGULATOR_MAX77693 and one current regulator 'CHARGER'. This is suitable for Exynos-4x12 chips. -config REGULATOR_PCAP - tristate "Motorola PCAP2 regulator driver" - depends on EZX_PCAP - help - This driver provides support for the voltage regulators of the - PCAP2 PMIC. +config REGULATOR_MC13XXX_CORE + tristate -config REGULATOR_LP3971 - tristate "National Semiconductors LP3971 PMIC regulator driver" - depends on I2C +config REGULATOR_MC13783 + tristate "Freescale MC13783 regulator driver" + depends on MFD_MC13783 + select REGULATOR_MC13XXX_CORE help - Say Y here to support the voltage regulators and convertors - on National Semiconductors LP3971 PMIC + Say y here to support the regulators found on the Freescale MC13783 + PMIC. -config REGULATOR_LP3972 - tristate "National Semiconductors LP3972 PMIC regulator driver" - depends on I2C +config REGULATOR_MC13892 + tristate "Freescale MC13892 regulator driver" + depends on MFD_MC13XXX + select REGULATOR_MC13XXX_CORE help - Say Y here to support the voltage regulators and convertors - on National Semiconductors LP3972 PMIC + Say y here to support the regulators found on the Freescale MC13892 + PMIC. -config REGULATOR_LP872X - bool "TI/National Semiconductor LP8720/LP8725 voltage regulators" - depends on I2C=y - select REGMAP_I2C +config REGULATOR_PALMAS + tristate "TI Palmas PMIC Regulators" + depends on MFD_PALMAS help - This driver supports LP8720/LP8725 PMIC + If you wish to control the regulators on the Palmas series of + chips say Y here. This will enable support for all the software + controllable SMPS/LDO regulators. -config REGULATOR_LP8755 - tristate "TI LP8755 High Performance PMU driver" - depends on I2C - select REGMAP_I2C - help - This driver supports LP8755 High Performance PMU driver. This - chip contains six step-down DC/DC converters which can support - 9 mode multiphase configuration. + The regulators available on Palmas series chips vary depending + on the muxing. This is handled automatically in the driver by + reading the mux info from OTP. -config REGULATOR_LP8788 - bool "TI LP8788 Power Regulators" - depends on MFD_LP8788 +config REGULATOR_PCAP + tristate "Motorola PCAP2 regulator driver" + depends on EZX_PCAP help - This driver supports LP8788 voltage regulator chip. + This driver provides support for the voltage regulators of the + PCAP2 PMIC. config REGULATOR_PCF50633 tristate "NXP PCF50633 regulator driver" @@ -309,6 +385,14 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and convertors on PCF50633 +config REGULATOR_PFUZE100 + tristate "Support regulators on Freescale PFUZE100 PMIC" + depends on I2C + select REGMAP_I2C + help + Say y here to support the regulators found on the Freescale PFUZE100 + PMIC. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 @@ -335,44 +419,15 @@ config REGULATOR_S5M8767 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and supports DVS mode with 8bits of output voltage control. -config REGULATOR_AB3100 - tristate "ST-Ericsson AB3100 Regulator functions" - depends on AB3100_CORE - default y if AB3100_CORE - help - These regulators correspond to functionality in the - AB3100 analog baseband dealing with power regulators - for the system. - -config REGULATOR_AB8500 - bool "ST-Ericsson AB8500 Power Regulators" - depends on AB8500_CORE - help - This driver supports the regulators found on the ST-Ericsson mixed - signal AB8500 PMIC - -config REGULATOR_DBX500_PRCMU - bool - -config REGULATOR_DB8500_PRCMU - bool "ST-Ericsson DB8500 Voltage Domain Regulators" - depends on MFD_DB8500_PRCMU - select REGULATOR_DBX500_PRCMU - help - This driver supports the voltage domain regulators controlled by the - DB8500 PRCMU - -config REGULATOR_PALMAS - tristate "TI Palmas PMIC Regulators" - depends on MFD_PALMAS +config REGULATOR_TI_ABB + tristate "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP help - If you wish to control the regulators on the Palmas series of - chips say Y here. This will enable support for all the software - controllable SMPS/LDO regulators. - - The regulators available on Palmas series chips vary depending - on the muxing. This is handled automatically in the driver by - reading the mux info from OTP. + Select this option to support Texas Instruments' on-chip Adaptive Body + Bias (ABB) LDO regulators. It is recommended that this option be + enabled on required TI SoC. Certain Operating Performance Points + on TI SoCs may be unstable without enabling this as it provides + device specific optimized bias to allow/optimize functionality. config REGULATOR_TPS51632 tristate "TI TPS51632 Power Regulator" @@ -475,22 +530,12 @@ config REGULATOR_TPS80031 output to control regulators. config REGULATOR_TWL4030 - bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" + tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" depends on TWL4030_CORE help This driver supports the voltage regulators provided by this family of companion chips. -config REGULATOR_TI_ABB - bool "TI Adaptive Body Bias on-chip LDO" - depends on ARCH_OMAP - help - Select this option to support Texas Instruments' on-chip Adaptive Body - Bias (ABB) LDO regulators. It is recommended that this option be - enabled on required TI SoC. Certain Operating Performance Points - on TI SoCs may be unstable without enabling this as it provides - device specific optimized bias to allow/optimize functionality. - config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG @@ -526,12 +571,5 @@ config REGULATOR_WM8994 This driver provides support for the voltage regulators on the WM8994 CODEC. -config REGULATOR_AS3711 - tristate "AS3711 PMIC" - depends on MFD_AS3711 - help - This driver provides support for the voltage regulators on the - AS3711 PMIC - endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ba4a3cf..185cce2 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -3,12 +3,13 @@ # -obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o +obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o obj-$(CONFIG_OF) += of_regulator.o obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o +obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o @@ -20,6 +21,8 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o +obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o +obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o @@ -46,12 +49,14 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o +obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o @@ -64,7 +69,6 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o -obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index 8b58763..881159d 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -174,7 +174,7 @@ static int aat2870_regulator_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.driver_data = ri; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 3be9e46..7d5eaa8 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -660,7 +660,7 @@ ab3100_regulator_of_probe(struct platform_device *pdev, struct device_node *np) static int ab3100_regulators_probe(struct platform_device *pdev) { - struct ab3100_platform_data *plfdata = pdev->dev.platform_data; + struct ab3100_platform_data *plfdata = dev_get_platdata(&pdev->dev); struct device_node *np = pdev->dev.of_node; int err = 0; u8 data; diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 6b981b5..b2b203c 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -214,7 +214,7 @@ MODULE_DEVICE_TABLE(i2c, ad5398_id); static int ad5398_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct regulator_init_data *init_data = client->dev.platform_data; + struct regulator_init_data *init_data = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct ad5398_chip_info *chip; const struct ad5398_current_data_format *df = diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 3da6bd6..8406cd7 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -30,102 +30,6 @@ struct as3711_regulator { struct regulator_dev *rdev; }; -static int as3711_list_voltage_sd(struct regulator_dev *rdev, - unsigned int selector) -{ - if (selector >= rdev->desc->n_voltages) - return -EINVAL; - - if (!selector) - return 0; - if (selector < 0x41) - return 600000 + selector * 12500; - if (selector < 0x71) - return 1400000 + (selector - 0x40) * 25000; - return 2600000 + (selector - 0x70) * 50000; -} - -static int as3711_list_voltage_aldo(struct regulator_dev *rdev, - unsigned int selector) -{ - if (selector >= rdev->desc->n_voltages) - return -EINVAL; - - if (selector < 0x10) - return 1200000 + selector * 50000; - return 1800000 + (selector - 0x10) * 100000; -} - -static int as3711_list_voltage_dldo(struct regulator_dev *rdev, - unsigned int selector) -{ - if (selector >= rdev->desc->n_voltages || - (selector > 0x10 && selector < 0x20)) - return -EINVAL; - - if (selector < 0x11) - return 900000 + selector * 50000; - return 1750000 + (selector - 0x20) * 50000; -} - -static int as3711_bound_check(struct regulator_dev *rdev, - int *min_uV, int *max_uV) -{ - struct as3711_regulator *reg = rdev_get_drvdata(rdev); - struct as3711_regulator_info *info = reg->reg_info; - - dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__, - *min_uV, rdev->desc->min_uV, info->max_uV); - - if (*max_uV < *min_uV || - *min_uV > info->max_uV || rdev->desc->min_uV > *max_uV) - return -EINVAL; - - if (rdev->desc->n_voltages == 1) - return 0; - - if (*max_uV > info->max_uV) - *max_uV = info->max_uV; - - if (*min_uV < rdev->desc->min_uV) - *min_uV = rdev->desc->min_uV; - - return *min_uV; -} - -static int as3711_sel_check(int min, int max, int bottom, int step) -{ - int sel, voltage; - - /* Round up min, when dividing: keeps us within the range */ - sel = DIV_ROUND_UP(min - bottom, step); - voltage = sel * step + bottom; - pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__, - min, max, bottom, step, sel); - if (voltage > max) - return -EINVAL; - - return sel; -} - -static int as3711_map_voltage_sd(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int ret; - - ret = as3711_bound_check(rdev, &min_uV, &max_uV); - if (ret <= 0) - return ret; - - if (min_uV <= 1400000) - return as3711_sel_check(min_uV, max_uV, 600000, 12500); - - if (min_uV <= 2600000) - return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40; - - return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70; -} - /* * The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and * STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes: @@ -180,44 +84,14 @@ static unsigned int as3711_get_mode_sd(struct regulator_dev *rdev) return -EINVAL; } -static int as3711_map_voltage_aldo(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int ret; - - ret = as3711_bound_check(rdev, &min_uV, &max_uV); - if (ret <= 0) - return ret; - - if (min_uV <= 1800000) - return as3711_sel_check(min_uV, max_uV, 1200000, 50000); - - return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10; -} - -static int as3711_map_voltage_dldo(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int ret; - - ret = as3711_bound_check(rdev, &min_uV, &max_uV); - if (ret <= 0) - return ret; - - if (min_uV <= 1700000) - return as3711_sel_check(min_uV, max_uV, 900000, 50000); - - return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20; -} - static struct regulator_ops as3711_sd_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = as3711_list_voltage_sd, - .map_voltage = as3711_map_voltage_sd, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, .get_mode = as3711_get_mode_sd, .set_mode = as3711_set_mode_sd, }; @@ -228,8 +102,8 @@ static struct regulator_ops as3711_aldo_ops = { .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = as3711_list_voltage_aldo, - .map_voltage = as3711_map_voltage_aldo, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, }; static struct regulator_ops as3711_dldo_ops = { @@ -238,8 +112,31 @@ static struct regulator_ops as3711_dldo_ops = { .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = as3711_list_voltage_dldo, - .map_voltage = as3711_map_voltage_dldo, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct regulator_linear_range as3711_sd_ranges[] = { + { .min_uV = 612500, .max_uV = 1400000, + .min_sel = 0x1, .max_sel = 0x40, .uV_step = 12500 }, + { .min_uV = 1425000, .max_uV = 2600000, + .min_sel = 0x41, .max_sel = 0x70, .uV_step = 25000 }, + { .min_uV = 2650000, .max_uV = 3350000, + .min_sel = 0x71, .max_sel = 0x7f, .uV_step = 50000 }, +}; + +static const struct regulator_linear_range as3711_aldo_ranges[] = { + { .min_uV = 1200000, .max_uV = 1950000, + .min_sel = 0, .max_sel = 0xf, .uV_step = 50000 }, + { .min_uV = 1800000, .max_uV = 3300000, + .min_sel = 0x10, .max_sel = 0x1f, .uV_step = 100000 }, +}; + +static const struct regulator_linear_range as3711_dldo_ranges[] = { + { .min_uV = 900000, .max_uV = 1700000, + .min_sel = 0, .max_sel = 0x10, .uV_step = 50000 }, + { .min_uV = 1750000, .max_uV = 3300000, + .min_sel = 0x20, .max_sel = 0x3f, .uV_step = 50000 }, }; #define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \ @@ -256,6 +153,8 @@ static struct regulator_ops as3711_dldo_ops = { .enable_reg = AS3711_ ## _en_reg, \ .enable_mask = BIT(_en_bit), \ .min_uV = _min_uV, \ + .linear_ranges = as3711_ ## _sfx ## _ranges, \ + .n_linear_ranges = ARRAY_SIZE(as3711_ ## _sfx ## _ranges), \ }, \ .max_uV = _max_uV, \ } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 288c75a..a01b8b3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -323,13 +323,14 @@ static ssize_t regulator_uA_show(struct device *dev, } static DEVICE_ATTR(microamps, 0444, regulator_uA_show, NULL); -static ssize_t regulator_name_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%s\n", rdev_get_name(rdev)); } +static DEVICE_ATTR_RO(name); static ssize_t regulator_print_opmode(char *buf, int mode) { @@ -489,15 +490,16 @@ static ssize_t regulator_total_uA_show(struct device *dev, } static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL); -static ssize_t regulator_num_users_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t num_users_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", rdev->use_count); } +static DEVICE_ATTR_RO(num_users); -static ssize_t regulator_type_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); @@ -509,6 +511,7 @@ static ssize_t regulator_type_show(struct device *dev, } return sprintf(buf, "unknown\n"); } +static DEVICE_ATTR_RO(type); static ssize_t regulator_suspend_mem_uV_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -632,12 +635,13 @@ static DEVICE_ATTR(bypass, 0444, * These are the only attributes are present for all regulators. * Other attributes are a function of regulator functionality. */ -static struct device_attribute regulator_dev_attrs[] = { - __ATTR(name, 0444, regulator_name_show, NULL), - __ATTR(num_users, 0444, regulator_num_users_show, NULL), - __ATTR(type, 0444, regulator_type_show, NULL), - __ATTR_NULL, +static struct attribute *regulator_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_num_users.attr, + &dev_attr_type.attr, + NULL, }; +ATTRIBUTE_GROUPS(regulator_dev); static void regulator_dev_release(struct device *dev) { @@ -648,7 +652,7 @@ static void regulator_dev_release(struct device *dev) static struct class regulator_class = { .name = "regulator", .dev_release = regulator_dev_release, - .dev_attrs = regulator_dev_attrs, + .dev_groups = regulator_dev_groups, }; /* Calculate the new optimum regulator operating mode based on the new total @@ -984,7 +988,8 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable) + && ops->set_ramp_delay) { ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); if (ret < 0) { rdev_err(rdev, "failed to set ramp_delay\n"); @@ -1238,7 +1243,7 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, - int exclusive) + bool exclusive) { struct regulator_dev *rdev; struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); @@ -1344,7 +1349,7 @@ out: */ struct regulator *regulator_get(struct device *dev, const char *id) { - return _regulator_get(dev, id, 0); + return _regulator_get(dev, id, false); } EXPORT_SYMBOL_GPL(regulator_get); @@ -1405,10 +1410,69 @@ EXPORT_SYMBOL_GPL(devm_regulator_get); */ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) { - return _regulator_get(dev, id, 1); + return _regulator_get(dev, id, true); } EXPORT_SYMBOL_GPL(regulator_get_exclusive); +/** + * regulator_get_optional - obtain optional access to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Other consumers will be + * unable to obtain this reference is held and the use count for the + * regulator will be initialised to reflect the current state of the + * regulator. + * + * This is intended for use by consumers for devices which can have + * some supplies unconnected in normal use, such as some MMC devices. + * It can allow the regulator core to provide stub supplies for other + * supplies requested using normal regulator_get() calls without + * disrupting the operation of drivers that can handle absent + * supplies. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get_optional(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 0); +} +EXPORT_SYMBOL_GPL(regulator_get_optional); + +/** + * devm_regulator_get_optional - Resource managed regulator_get_optional() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_optional(). Regulators returned from this + * function are automatically regulator_put() on driver detach. See + * regulator_get_optional() for more information. + */ +struct regulator *devm_regulator_get_optional(struct device *dev, + const char *id) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regulator = regulator_get_optional(dev, id); + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} +EXPORT_SYMBOL_GPL(devm_regulator_get_optional); + /* Locks held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { @@ -1435,6 +1499,36 @@ static void _regulator_put(struct regulator *regulator) } /** + * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_exclusive(). Regulators returned from this function + * are automatically regulator_put() on driver detach. See regulator_get() for + * more information. + */ +struct regulator *devm_regulator_get_exclusive(struct device *dev, + const char *id) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regulator = _regulator_get(dev, id, 1); + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} +EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); + +/** * regulator_put - "free" the regulator source * @regulator: regulator source * @@ -1890,8 +1984,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) rdev->deferred_disables++; mutex_unlock(&rdev->mutex); - ret = schedule_delayed_work(&rdev->disable_work, - msecs_to_jiffies(ms)); + ret = queue_delayed_work(system_power_efficient_wq, + &rdev->disable_work, + msecs_to_jiffies(ms)); if (ret < 0) return ret; else @@ -1899,77 +1994,6 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) } EXPORT_SYMBOL_GPL(regulator_disable_deferred); -/** - * regulator_is_enabled_regmap - standard is_enabled() for regmap users - * - * @rdev: regulator to operate on - * - * Regulators that use regmap for their register I/O can set the - * enable_reg and enable_mask fields in their descriptor and then use - * this as their is_enabled operation, saving some code. - */ -int regulator_is_enabled_regmap(struct regulator_dev *rdev) -{ - unsigned int val; - int ret; - - ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); - if (ret != 0) - return ret; - - if (rdev->desc->enable_is_inverted) - return (val & rdev->desc->enable_mask) == 0; - else - return (val & rdev->desc->enable_mask) != 0; -} -EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); - -/** - * regulator_enable_regmap - standard enable() for regmap users - * - * @rdev: regulator to operate on - * - * Regulators that use regmap for their register I/O can set the - * enable_reg and enable_mask fields in their descriptor and then use - * this as their enable() operation, saving some code. - */ -int regulator_enable_regmap(struct regulator_dev *rdev) -{ - unsigned int val; - - if (rdev->desc->enable_is_inverted) - val = 0; - else - val = rdev->desc->enable_mask; - - return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); -} -EXPORT_SYMBOL_GPL(regulator_enable_regmap); - -/** - * regulator_disable_regmap - standard disable() for regmap users - * - * @rdev: regulator to operate on - * - * Regulators that use regmap for their register I/O can set the - * enable_reg and enable_mask fields in their descriptor and then use - * this as their disable() operation, saving some code. - */ -int regulator_disable_regmap(struct regulator_dev *rdev) -{ - unsigned int val; - - if (rdev->desc->enable_is_inverted) - val = rdev->desc->enable_mask; - else - val = 0; - - return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); -} -EXPORT_SYMBOL_GPL(regulator_disable_regmap); - static int _regulator_is_enabled(struct regulator_dev *rdev) { /* A GPIO control always takes precedence */ @@ -2055,55 +2079,6 @@ int regulator_count_voltages(struct regulator *regulator) EXPORT_SYMBOL_GPL(regulator_count_voltages); /** - * regulator_list_voltage_linear - List voltages with simple calculation - * - * @rdev: Regulator device - * @selector: Selector to convert into a voltage - * - * Regulators with a simple linear mapping between voltages and - * selectors can set min_uV and uV_step in the regulator descriptor - * and then use this function as their list_voltage() operation, - */ -int regulator_list_voltage_linear(struct regulator_dev *rdev, - unsigned int selector) -{ - if (selector >= rdev->desc->n_voltages) - return -EINVAL; - if (selector < rdev->desc->linear_min_sel) - return 0; - - selector -= rdev->desc->linear_min_sel; - - return rdev->desc->min_uV + (rdev->desc->uV_step * selector); -} -EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); - -/** - * regulator_list_voltage_table - List voltages with table based mapping - * - * @rdev: Regulator device - * @selector: Selector to convert into a voltage - * - * Regulators with table based mapping between voltages and - * selectors can set volt_table in the regulator descriptor - * and then use this function as their list_voltage() operation. - */ -int regulator_list_voltage_table(struct regulator_dev *rdev, - unsigned int selector) -{ - if (!rdev->desc->volt_table) { - BUG_ON(!rdev->desc->volt_table); - return -EINVAL; - } - - if (selector >= rdev->desc->n_voltages) - return -EINVAL; - - return rdev->desc->volt_table[selector]; -} -EXPORT_SYMBOL_GPL(regulator_list_voltage_table); - -/** * regulator_list_voltage - enumerate supported voltages * @regulator: regulator source * @selector: identify voltage to list @@ -2197,177 +2172,6 @@ int regulator_is_supported_voltage(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_is_supported_voltage); -/** - * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users - * - * @rdev: regulator to operate on - * - * Regulators that use regmap for their register I/O can set the - * vsel_reg and vsel_mask fields in their descriptor and then use this - * as their get_voltage_vsel operation, saving some code. - */ -int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev) -{ - unsigned int val; - int ret; - - ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); - if (ret != 0) - return ret; - - val &= rdev->desc->vsel_mask; - val >>= ffs(rdev->desc->vsel_mask) - 1; - - return val; -} -EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap); - -/** - * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users - * - * @rdev: regulator to operate on - * @sel: Selector to set - * - * Regulators that use regmap for their register I/O can set the - * vsel_reg and vsel_mask fields in their descriptor and then use this - * as their set_voltage_vsel operation, saving some code. - */ -int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel) -{ - int ret; - - sel <<= ffs(rdev->desc->vsel_mask) - 1; - - ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, - rdev->desc->vsel_mask, sel); - if (ret) - return ret; - - if (rdev->desc->apply_bit) - ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg, - rdev->desc->apply_bit, - rdev->desc->apply_bit); - return ret; -} -EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap); - -/** - * regulator_map_voltage_iterate - map_voltage() based on list_voltage() - * - * @rdev: Regulator to operate on - * @min_uV: Lower bound for voltage - * @max_uV: Upper bound for voltage - * - * Drivers implementing set_voltage_sel() and list_voltage() can use - * this as their map_voltage() operation. It will find a suitable - * voltage by calling list_voltage() until it gets something in bounds - * for the requested voltages. - */ -int regulator_map_voltage_iterate(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int best_val = INT_MAX; - int selector = 0; - int i, ret; - - /* Find the smallest voltage that falls within the specified - * range. - */ - for (i = 0; i < rdev->desc->n_voltages; i++) { - ret = rdev->desc->ops->list_voltage(rdev, i); - if (ret < 0) - continue; - - if (ret < best_val && ret >= min_uV && ret <= max_uV) { - best_val = ret; - selector = i; - } - } - - if (best_val != INT_MAX) - return selector; - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate); - -/** - * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list - * - * @rdev: Regulator to operate on - * @min_uV: Lower bound for voltage - * @max_uV: Upper bound for voltage - * - * Drivers that have ascendant voltage list can use this as their - * map_voltage() operation. - */ -int regulator_map_voltage_ascend(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int i, ret; - - for (i = 0; i < rdev->desc->n_voltages; i++) { - ret = rdev->desc->ops->list_voltage(rdev, i); - if (ret < 0) - continue; - - if (ret > max_uV) - break; - - if (ret >= min_uV && ret <= max_uV) - return i; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend); - -/** - * regulator_map_voltage_linear - map_voltage() for simple linear mappings - * - * @rdev: Regulator to operate on - * @min_uV: Lower bound for voltage - * @max_uV: Upper bound for voltage - * - * Drivers providing min_uV and uV_step in their regulator_desc can - * use this as their map_voltage() operation. - */ -int regulator_map_voltage_linear(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int ret, voltage; - - /* Allow uV_step to be 0 for fixed voltage */ - if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) { - if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV) - return 0; - else - return -EINVAL; - } - - if (!rdev->desc->uV_step) { - BUG_ON(!rdev->desc->uV_step); - return -EINVAL; - } - - if (min_uV < rdev->desc->min_uV) - min_uV = rdev->desc->min_uV; - - ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step); - if (ret < 0) - return ret; - - ret += rdev->desc->linear_min_sel; - - /* Map back into a voltage to verify we're still in bounds */ - voltage = rdev->desc->ops->list_voltage(rdev, ret); - if (voltage < min_uV || voltage > max_uV) - return -EINVAL; - - return ret; -} -EXPORT_SYMBOL_GPL(regulator_map_voltage_linear); - static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { @@ -2438,8 +2242,8 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 && - old_selector != selector && rdev->desc->ops->set_voltage_time_sel) { + if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0 + && old_selector != selector) { delay = rdev->desc->ops->set_voltage_time_sel(rdev, old_selector, selector); @@ -2971,47 +2775,6 @@ out: EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); /** - * regulator_set_bypass_regmap - Default set_bypass() using regmap - * - * @rdev: device to operate on. - * @enable: state to set. - */ -int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) -{ - unsigned int val; - - if (enable) - val = rdev->desc->bypass_mask; - else - val = 0; - - return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, - rdev->desc->bypass_mask, val); -} -EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); - -/** - * regulator_get_bypass_regmap - Default get_bypass() using regmap - * - * @rdev: device to operate on. - * @enable: current state. - */ -int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) -{ - unsigned int val; - int ret; - - ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); - if (ret != 0) - return ret; - - *enable = val & rdev->desc->bypass_mask; - - return 0; -} -EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); - -/** * regulator_allow_bypass - allow the regulator to go into bypass mode * * @regulator: Regulator to configure @@ -3740,8 +3503,11 @@ void regulator_unregister(struct regulator_dev *rdev) if (rdev == NULL) return; - if (rdev->supply) + if (rdev->supply) { + while (rdev->use_count--) + regulator_disable(rdev->supply); regulator_put(rdev->supply); + } mutex_lock(®ulator_list_mutex); debugfs_remove_recursive(rdev->debugfs); flush_work(&rdev->disable_work.work); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 2afa573..f06854c 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -252,39 +252,12 @@ static int da9034_set_dvc_voltage_sel(struct regulator_dev *rdev, return ret; } -static int da9034_map_ldo12_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - struct da903x_regulator_info *info = rdev_get_drvdata(rdev); - int sel; - - if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); - return -EINVAL; - } - - sel = DIV_ROUND_UP(min_uV - info->desc.min_uV, info->desc.uV_step); - sel = (sel >= 20) ? sel - 12 : ((sel > 7) ? 8 : sel); - - return sel; -} - -static int da9034_list_ldo12_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - struct da903x_regulator_info *info = rdev_get_drvdata(rdev); - int volt; - - if (selector >= 8) - volt = 2700000 + rdev->desc->uV_step * (selector - 8); - else - volt = rdev->desc->min_uV + rdev->desc->uV_step * selector; - - if (volt > info->max_uV) - return -EINVAL; - - return volt; -} +static const struct regulator_linear_range da9034_ldo12_ranges[] = { + { .min_uV = 1700000, .max_uV = 2050000, .min_sel = 0, .max_sel = 7, + .uV_step = 50000 }, + { .min_uV = 2700000, .max_uV = 3050000, .min_sel = 8, .max_sel = 15, + .uV_step = 50000 }, +}; static struct regulator_ops da903x_regulator_ldo_ops = { .set_voltage_sel = da903x_set_voltage_sel, @@ -332,8 +305,8 @@ static struct regulator_ops da9034_regulator_dvc_ops = { static struct regulator_ops da9034_regulator_ldo12_ops = { .set_voltage_sel = da903x_set_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, - .list_voltage = da9034_list_ldo12_voltage, - .map_voltage = da9034_map_ldo12_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -476,6 +449,8 @@ static int da903x_regulator_probe(struct platform_device *pdev) if (ri->desc.id == DA9034_ID_LDO12) { ri->desc.ops = &da9034_regulator_ldo12_ops; ri->desc.n_voltages = 16; + ri->desc.linear_ranges = da9034_ldo12_ranges; + ri->desc.n_linear_ranges = ARRAY_SIZE(da9034_ldo12_ranges); } if (ri->desc.id == DA9030_ID_LDO14) @@ -485,7 +460,7 @@ static int da903x_regulator_probe(struct platform_device *pdev) ri->desc.ops = &da9030_regulator_ldo1_15_ops; config.dev = &pdev->dev; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = ri; rdev = regulator_register(&ri->desc, &config); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 96b569a..1e4d483 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -349,7 +349,7 @@ static int da9052_regulator_probe(struct platform_device *pdev) return -ENOMEM; da9052 = dev_get_drvdata(pdev->dev.parent); - pdata = da9052->dev->platform_data; + pdata = dev_get_platdata(da9052->dev); regulator->da9052 = da9052; regulator->info = find_regulator_info(regulator->da9052->chip_id, diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index 3022109..77b53e5 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -535,7 +535,7 @@ static int da9055_regulator_probe(struct platform_device *pdev) struct regulator_config config = { }; struct da9055_regulator *regulator; struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent); - struct da9055_pdata *pdata = da9055->dev->platform_data; + struct da9055_pdata *pdata = dev_get_platdata(da9055->dev); int ret, irq; if (pdata == NULL || pdata->regulators[pdev->id] == NULL) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c new file mode 100644 index 0000000..1a78163 --- /dev/null +++ b/drivers/regulator/da9063-regulator.c @@ -0,0 +1,934 @@ +/* + * Regulator driver for DA9063 PMIC series + * + * Copyright 2012 Dialog Semiconductors Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.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/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + + +/* Definition for registering regmap bit fields using a mask */ +#define BFIELD(_reg, _mask) \ + REG_FIELD(_reg, __builtin_ffs((int)_mask) - 1, \ + sizeof(unsigned int) * 8 - __builtin_clz((_mask)) - 1) + +/* Regulator capabilities and registers description */ +struct da9063_regulator_info { + struct regulator_desc desc; + + /* Current limiting */ + unsigned n_current_limits; + const int *current_limits; + + /* DA9063 main register fields */ + struct reg_field mode; /* buck mode of operation */ + struct reg_field suspend; + struct reg_field sleep; + struct reg_field suspend_sleep; + unsigned int suspend_vsel_reg; + struct reg_field ilimit; + + /* DA9063 event detection bit */ + struct reg_field oc_event; +}; + +/* Macros for LDO */ +#define DA9063_LDO(chip, regl_name, min_mV, step_mV, max_mV) \ + .desc.id = chip##_ID_##regl_name, \ + .desc.name = __stringify(chip##_##regl_name), \ + .desc.ops = &da9063_ldo_ops, \ + .desc.min_uV = (min_mV) * 1000, \ + .desc.uV_step = (step_mV) * 1000, \ + .desc.n_voltages = (((max_mV) - (min_mV))/(step_mV) + 1), \ + .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \ + .desc.enable_mask = DA9063_LDO_EN, \ + .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \ + .desc.vsel_mask = DA9063_V##regl_name##_MASK, \ + .desc.linear_min_sel = DA9063_V##regl_name##_BIAS, \ + .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_LDO_SL), \ + .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_LDO_SL), \ + .suspend_vsel_reg = DA9063_REG_V##regl_name##_B + +/* Macros for voltage DC/DC converters (BUCKs) */ +#define DA9063_BUCK(chip, regl_name, min_mV, step_mV, max_mV, limits_array) \ + .desc.id = chip##_ID_##regl_name, \ + .desc.name = __stringify(chip##_##regl_name), \ + .desc.ops = &da9063_buck_ops, \ + .desc.min_uV = (min_mV) * 1000, \ + .desc.uV_step = (step_mV) * 1000, \ + .desc.n_voltages = ((max_mV) - (min_mV))/(step_mV) + 1, \ + .current_limits = limits_array, \ + .n_current_limits = ARRAY_SIZE(limits_array) + +#define DA9063_BUCK_COMMON_FIELDS(regl_name) \ + .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \ + .desc.enable_mask = DA9063_BUCK_EN, \ + .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \ + .desc.vsel_mask = DA9063_VBUCK_MASK, \ + .desc.linear_min_sel = DA9063_VBUCK_BIAS, \ + .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_BUCK_SL), \ + .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_BUCK_SL), \ + .suspend_vsel_reg = DA9063_REG_V##regl_name##_B, \ + .mode = BFIELD(DA9063_REG_##regl_name##_CFG, DA9063_BUCK_MODE_MASK) + +/* Defines asignment of regulators info table to chip model */ +struct da9063_dev_model { + const struct da9063_regulator_info *regulator_info; + unsigned n_regulators; + unsigned dev_model; +}; + +/* Single regulator settings */ +struct da9063_regulator { + struct regulator_desc desc; + struct regulator_dev *rdev; + struct da9063 *hw; + const struct da9063_regulator_info *info; + + struct regmap_field *mode; + struct regmap_field *suspend; + struct regmap_field *sleep; + struct regmap_field *suspend_sleep; + struct regmap_field *ilimit; +}; + +/* Encapsulates all information for the regulators driver */ +struct da9063_regulators { + int irq_ldo_lim; + int irq_uvov; + + unsigned n_regulators; + /* Array size to be defined during init. Keep at end. */ + struct da9063_regulator regulator[0]; +}; + +/* BUCK modes for DA9063 */ +enum { + BUCK_MODE_MANUAL, /* 0 */ + BUCK_MODE_SLEEP, /* 1 */ + BUCK_MODE_SYNC, /* 2 */ + BUCK_MODE_AUTO /* 3 */ +}; + +/* Regulator operations */ + +/* Current limits array (in uA) for BCORE1, BCORE2, BPRO. + Entry indexes corresponds to register values. */ +static const int da9063_buck_a_limits[] = { + 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000, + 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000 +}; + +/* Current limits array (in uA) for BMEM, BIO, BPERI. + Entry indexes corresponds to register values. */ +static const int da9063_buck_b_limits[] = { + 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000, + 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000 +}; + +/* Current limits array (in uA) for merged BCORE1 and BCORE2. + Entry indexes corresponds to register values. */ +static const int da9063_bcores_merged_limits[] = { + 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000, + 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000 +}; + +/* Current limits array (in uA) for merged BMEM and BIO. + Entry indexes corresponds to register values. */ +static const int da9063_bmem_bio_merged_limits[] = { + 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, + 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000 +}; + +static int da9063_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + const struct da9063_regulator_info *rinfo = regl->info; + int n, tval; + + for (n = 0; n < rinfo->n_current_limits; n++) { + tval = rinfo->current_limits[n]; + if (tval >= min_uA && tval <= max_uA) + return regmap_field_write(regl->ilimit, n); + } + + return -EINVAL; +} + +static int da9063_get_current_limit(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + const struct da9063_regulator_info *rinfo = regl->info; + unsigned int sel; + int ret; + + ret = regmap_field_read(regl->ilimit, &sel); + if (ret < 0) + return ret; + + if (sel >= rinfo->n_current_limits) + sel = rinfo->n_current_limits - 1; + + return rinfo->current_limits[sel]; +} + +static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + unsigned val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = BUCK_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return regmap_field_write(regl->mode, val); +} + +/* + * Bucks use single mode register field for normal operation + * and suspend state. + * There are 3 modes to map to: FAST, NORMAL, and STANDBY. + */ + +static unsigned da9063_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + struct regmap_field *field; + unsigned int val, mode = 0; + int ret; + + ret = regmap_field_read(regl->mode, &val); + if (ret < 0) + return ret; + + switch (val) { + default: + case BUCK_MODE_MANUAL: + mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY; + /* Sleep flag bit decides the mode */ + break; + case BUCK_MODE_SLEEP: + return REGULATOR_MODE_STANDBY; + case BUCK_MODE_SYNC: + return REGULATOR_MODE_FAST; + case BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + } + + /* Detect current regulator state */ + ret = regmap_field_read(regl->suspend, &val); + if (ret < 0) + return 0; + + /* Read regulator mode from proper register, depending on state */ + if (val) + field = regl->suspend_sleep; + else + field = regl->sleep; + + ret = regmap_field_read(field, &val); + if (ret < 0) + return 0; + + if (val) + mode &= REGULATOR_MODE_STANDBY; + else + mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST; + + return mode; +} + +/* + * LDOs use sleep flags - one for normal and one for suspend state. + * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state. + */ + +static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + unsigned val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = 0; + break; + case REGULATOR_MODE_STANDBY: + val = 1; + break; + default: + return -EINVAL; + } + + return regmap_field_write(regl->sleep, val); +} + +static unsigned da9063_ldo_get_mode(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + struct regmap_field *field; + int ret, val; + + /* Detect current regulator state */ + ret = regmap_field_read(regl->suspend, &val); + if (ret < 0) + return 0; + + /* Read regulator mode from proper register, depending on state */ + if (val) + field = regl->suspend_sleep; + else + field = regl->sleep; + + ret = regmap_field_read(field, &val); + if (ret < 0) + return 0; + + if (val) + return REGULATOR_MODE_STANDBY; + else + return REGULATOR_MODE_NORMAL; +} + +static int da9063_buck_get_status(struct regulator_dev *rdev) +{ + int ret = regulator_is_enabled_regmap(rdev); + + if (ret == 0) { + ret = REGULATOR_STATUS_OFF; + } else if (ret > 0) { + ret = da9063_buck_get_mode(rdev); + if (ret > 0) + ret = regulator_mode_to_status(ret); + else if (ret == 0) + ret = -EIO; + } + + return ret; +} + +static int da9063_ldo_get_status(struct regulator_dev *rdev) +{ + int ret = regulator_is_enabled_regmap(rdev); + + if (ret == 0) { + ret = REGULATOR_STATUS_OFF; + } else if (ret > 0) { + ret = da9063_ldo_get_mode(rdev); + if (ret > 0) + ret = regulator_mode_to_status(ret); + else if (ret == 0) + ret = -EIO; + } + + return ret; +} + +static int da9063_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + const struct da9063_regulator_info *rinfo = regl->info; + int ret, sel; + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return -EINVAL; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg, + rdev->desc->vsel_mask, sel); + + return ret; +} + +static int da9063_suspend_enable(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + + return regmap_field_write(regl->suspend, 1); +} + +static int da9063_suspend_disable(struct regulator_dev *rdev) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + + return regmap_field_write(regl->suspend, 0); +} + +static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = BUCK_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return regmap_field_write(regl->mode, val); +} + +static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct da9063_regulator *regl = rdev_get_drvdata(rdev); + unsigned val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = 0; + break; + case REGULATOR_MODE_STANDBY: + val = 1; + break; + default: + return -EINVAL; + } + + return regmap_field_write(regl->suspend_sleep, val); +} + +static struct regulator_ops da9063_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = da9063_set_current_limit, + .get_current_limit = da9063_get_current_limit, + .set_mode = da9063_buck_set_mode, + .get_mode = da9063_buck_get_mode, + .get_status = da9063_buck_get_status, + .set_suspend_voltage = da9063_set_suspend_voltage, + .set_suspend_enable = da9063_suspend_enable, + .set_suspend_disable = da9063_suspend_disable, + .set_suspend_mode = da9063_buck_set_suspend_mode, +}; + +static struct regulator_ops da9063_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_mode = da9063_ldo_set_mode, + .get_mode = da9063_ldo_get_mode, + .get_status = da9063_ldo_get_status, + .set_suspend_voltage = da9063_set_suspend_voltage, + .set_suspend_enable = da9063_suspend_enable, + .set_suspend_disable = da9063_suspend_disable, + .set_suspend_mode = da9063_ldo_set_suspend_mode, +}; + +/* Info of regulators for DA9063 */ +static const struct da9063_regulator_info da9063_regulator_info[] = { + { + DA9063_BUCK(DA9063, BCORE1, 300, 10, 1570, + da9063_buck_a_limits), + DA9063_BUCK_COMMON_FIELDS(BCORE1), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C, + DA9063_BCORE1_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BCORE2, 300, 10, 1570, + da9063_buck_a_limits), + DA9063_BUCK_COMMON_FIELDS(BCORE2), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE2_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C, + DA9063_BCORE2_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BPRO, 530, 10, 1800, + da9063_buck_a_limits), + DA9063_BUCK_COMMON_FIELDS(BPRO), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPRO_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B, + DA9063_BPRO_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BMEM, 800, 20, 3340, + da9063_buck_b_limits), + DA9063_BUCK_COMMON_FIELDS(BMEM), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A, + DA9063_BMEM_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BIO, 800, 20, 3340, + da9063_buck_b_limits), + DA9063_BUCK_COMMON_FIELDS(BIO), + .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VBIO_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A, + DA9063_BIO_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BPERI, 800, 20, 3340, + da9063_buck_b_limits), + DA9063_BUCK_COMMON_FIELDS(BPERI), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPERI_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B, + DA9063_BPERI_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BCORES_MERGED, 300, 10, 1570, + da9063_bcores_merged_limits), + /* BCORES_MERGED uses the same register fields as BCORE1 */ + DA9063_BUCK_COMMON_FIELDS(BCORE1), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C, + DA9063_BCORE1_ILIM_MASK), + }, + { + DA9063_BUCK(DA9063, BMEM_BIO_MERGED, 800, 20, 3340, + da9063_bmem_bio_merged_limits), + /* BMEM_BIO_MERGED uses the same register fields as BMEM */ + DA9063_BUCK_COMMON_FIELDS(BMEM), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL), + .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A, + DA9063_BMEM_ILIM_MASK), + }, + { + DA9063_LDO(DA9063, LDO1, 600, 20, 1860), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO1_SEL), + }, + { + DA9063_LDO(DA9063, LDO2, 600, 20, 1860), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO2_SEL), + }, + { + DA9063_LDO(DA9063, LDO3, 900, 20, 3440), + .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO3_SEL), + .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO3_LIM), + }, + { + DA9063_LDO(DA9063, LDO4, 900, 20, 3440), + .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VLDO4_SEL), + .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO4_LIM), + }, + { + DA9063_LDO(DA9063, LDO5, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO5_CONT, DA9063_VLDO5_SEL), + }, + { + DA9063_LDO(DA9063, LDO6, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO6_CONT, DA9063_VLDO6_SEL), + }, + { + DA9063_LDO(DA9063, LDO7, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO7_CONT, DA9063_VLDO7_SEL), + .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO7_LIM), + }, + { + DA9063_LDO(DA9063, LDO8, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO8_CONT, DA9063_VLDO8_SEL), + .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO8_LIM), + }, + { + DA9063_LDO(DA9063, LDO9, 950, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO9_CONT, DA9063_VLDO9_SEL), + }, + { + DA9063_LDO(DA9063, LDO10, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO10_CONT, DA9063_VLDO10_SEL), + }, + { + DA9063_LDO(DA9063, LDO11, 900, 50, 3600), + .suspend = BFIELD(DA9063_REG_LDO11_CONT, DA9063_VLDO11_SEL), + .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO11_LIM), + }, +}; + +/* Link chip model with regulators info table */ +static struct da9063_dev_model regulators_models[] = { + { + .regulator_info = da9063_regulator_info, + .n_regulators = ARRAY_SIZE(da9063_regulator_info), + .dev_model = PMIC_DA9063, + }, + { } +}; + +/* Regulator interrupt handlers */ +static irqreturn_t da9063_ldo_lim_event(int irq, void *data) +{ + struct da9063_regulators *regulators = data; + struct da9063 *hw = regulators->regulator[0].hw; + struct da9063_regulator *regl; + int bits, i , ret; + + ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits); + if (ret < 0) + return IRQ_NONE; + + for (i = regulators->n_regulators - 1; i >= 0; i--) { + regl = ®ulators->regulator[i]; + if (regl->info->oc_event.reg != DA9063_REG_STATUS_D) + continue; + + if (BIT(regl->info->oc_event.lsb) & bits) + regulator_notifier_call_chain(regl->rdev, + REGULATOR_EVENT_OVER_CURRENT, NULL); + } + + return IRQ_HANDLED; +} + +/* + * Probing and Initialisation functions + */ +static const struct regulator_init_data *da9063_get_regulator_initdata( + const struct da9063_regulators_pdata *regl_pdata, int id) +{ + int i; + + for (i = 0; i < regl_pdata->n_regulators; i++) { + if (id == regl_pdata->regulator_data[i].id) + return regl_pdata->regulator_data[i].initdata; + } + + return NULL; +} + +#ifdef CONFIG_OF +static struct of_regulator_match da9063_matches[] = { + [DA9063_ID_BCORE1] = { .name = "bcore1" }, + [DA9063_ID_BCORE2] = { .name = "bcore2" }, + [DA9063_ID_BPRO] = { .name = "bpro", }, + [DA9063_ID_BMEM] = { .name = "bmem", }, + [DA9063_ID_BIO] = { .name = "bio", }, + [DA9063_ID_BPERI] = { .name = "bperi", }, + [DA9063_ID_BCORES_MERGED] = { .name = "bcores-merged" }, + [DA9063_ID_BMEM_BIO_MERGED] = { .name = "bmem-bio-merged", }, + [DA9063_ID_LDO1] = { .name = "ldo1", }, + [DA9063_ID_LDO2] = { .name = "ldo2", }, + [DA9063_ID_LDO3] = { .name = "ldo3", }, + [DA9063_ID_LDO4] = { .name = "ldo4", }, + [DA9063_ID_LDO5] = { .name = "ldo5", }, + [DA9063_ID_LDO6] = { .name = "ldo6", }, + [DA9063_ID_LDO7] = { .name = "ldo7", }, + [DA9063_ID_LDO8] = { .name = "ldo8", }, + [DA9063_ID_LDO9] = { .name = "ldo9", }, + [DA9063_ID_LDO10] = { .name = "ldo10", }, + [DA9063_ID_LDO11] = { .name = "ldo11", }, +}; + +static struct da9063_regulators_pdata *da9063_parse_regulators_dt( + struct platform_device *pdev, + struct of_regulator_match **da9063_reg_matches) +{ + struct da9063_regulators_pdata *pdata; + struct da9063_regulator_data *rdata; + struct device_node *node; + int i, n, num; + + node = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + if (!node) { + dev_err(&pdev->dev, "Regulators device node not found\n"); + return ERR_PTR(-ENODEV); + } + + num = of_regulator_match(&pdev->dev, node, da9063_matches, + ARRAY_SIZE(da9063_matches)); + if (num < 0) { + dev_err(&pdev->dev, "Failed to match regulators\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->regulator_data = devm_kzalloc(&pdev->dev, + num * sizeof(*pdata->regulator_data), + GFP_KERNEL); + if (!pdata->regulator_data) + return ERR_PTR(-ENOMEM); + pdata->n_regulators = num; + + n = 0; + for (i = 0; i < ARRAY_SIZE(da9063_matches); i++) { + if (!da9063_matches[i].init_data) + continue; + + rdata = &pdata->regulator_data[n]; + rdata->id = i; + rdata->initdata = da9063_matches[i].init_data; + + n++; + }; + + *da9063_reg_matches = da9063_matches; + return pdata; +} +#else +static struct da9063_regulators_pdata *da9063_parse_regulators_dt( + struct platform_device *pdev, + struct of_regulator_match **da9063_reg_matches) +{ + da9063_reg_matches = NULL; + return PTR_ERR(-ENODEV); +} +#endif + +static int da9063_regulator_probe(struct platform_device *pdev) +{ + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_pdata *da9063_pdata = dev_get_platdata(da9063->dev); + struct of_regulator_match *da9063_reg_matches; + struct da9063_regulators_pdata *regl_pdata; + const struct da9063_dev_model *model; + struct da9063_regulators *regulators; + struct da9063_regulator *regl; + struct regulator_config config; + bool bcores_merged, bmem_bio_merged; + int id, irq, n, n_regulators, ret, val; + size_t size; + + regl_pdata = da9063_pdata ? da9063_pdata->regulators_pdata : NULL; + + if (!regl_pdata) + regl_pdata = da9063_parse_regulators_dt(pdev, + &da9063_reg_matches); + + if (IS_ERR(regl_pdata) || regl_pdata->n_regulators == 0) { + dev_err(&pdev->dev, + "No regulators defined for the platform\n"); + return PTR_ERR(regl_pdata); + } + + /* Find regulators set for particular device model */ + for (model = regulators_models; model->regulator_info; model++) { + if (model->dev_model == da9063->model) + break; + } + if (!model->regulator_info) { + dev_err(&pdev->dev, "Chip model not recognised (%u)\n", + da9063->model); + return -ENODEV; + } + + ret = regmap_read(da9063->regmap, DA9063_REG_CONFIG_H, &val); + if (ret < 0) { + dev_err(&pdev->dev, + "Error while reading BUCKs configuration\n"); + return -EIO; + } + bcores_merged = val & DA9063_BCORE_MERGE; + bmem_bio_merged = val & DA9063_BUCK_MERGE; + + n_regulators = model->n_regulators; + if (bcores_merged) + n_regulators -= 2; /* remove BCORE1, BCORE2 */ + else + n_regulators--; /* remove BCORES_MERGED */ + if (bmem_bio_merged) + n_regulators -= 2; /* remove BMEM, BIO */ + else + n_regulators--; /* remove BMEM_BIO_MERGED */ + + /* Allocate memory required by usable regulators */ + size = sizeof(struct da9063_regulators) + + n_regulators * sizeof(struct da9063_regulator); + regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!regulators) { + dev_err(&pdev->dev, "No memory for regulators\n"); + return -ENOMEM; + } + + regulators->n_regulators = n_regulators; + platform_set_drvdata(pdev, regulators); + + /* Register all regulators declared in platform information */ + n = 0; + id = 0; + while (n < regulators->n_regulators) { + /* Skip regulator IDs depending on merge mode configuration */ + switch (id) { + case DA9063_ID_BCORE1: + case DA9063_ID_BCORE2: + if (bcores_merged) { + id++; + continue; + } + break; + case DA9063_ID_BMEM: + case DA9063_ID_BIO: + if (bmem_bio_merged) { + id++; + continue; + } + break; + case DA9063_ID_BCORES_MERGED: + if (!bcores_merged) { + id++; + continue; + } + break; + case DA9063_ID_BMEM_BIO_MERGED: + if (!bmem_bio_merged) { + id++; + continue; + } + break; + } + + /* Initialise regulator structure */ + regl = ®ulators->regulator[n]; + regl->hw = da9063; + regl->info = &model->regulator_info[id]; + regl->desc = regl->info->desc; + regl->desc.type = REGULATOR_VOLTAGE; + regl->desc.owner = THIS_MODULE; + + if (regl->info->mode.reg) + regl->mode = devm_regmap_field_alloc(&pdev->dev, + da9063->regmap, regl->info->mode); + if (regl->info->suspend.reg) + regl->suspend = devm_regmap_field_alloc(&pdev->dev, + da9063->regmap, regl->info->suspend); + if (regl->info->sleep.reg) + regl->sleep = devm_regmap_field_alloc(&pdev->dev, + da9063->regmap, regl->info->sleep); + if (regl->info->suspend_sleep.reg) + regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev, + da9063->regmap, regl->info->suspend_sleep); + if (regl->info->ilimit.reg) + regl->ilimit = devm_regmap_field_alloc(&pdev->dev, + da9063->regmap, regl->info->ilimit); + + /* Register regulator */ + memset(&config, 0, sizeof(config)); + config.dev = &pdev->dev; + config.init_data = da9063_get_regulator_initdata(regl_pdata, id); + config.driver_data = regl; + if (da9063_reg_matches) + config.of_node = da9063_reg_matches[id].of_node; + config.regmap = da9063->regmap; + regl->rdev = regulator_register(®l->desc, &config); + if (IS_ERR(regl->rdev)) { + dev_err(&pdev->dev, + "Failed to register %s regulator\n", + regl->desc.name); + ret = PTR_ERR(regl->rdev); + goto err; + } + id++; + n++; + } + + /* LDOs overcurrent event support */ + irq = platform_get_irq_byname(pdev, "LDO_LIM"); + if (irq < 0) { + ret = irq; + dev_err(&pdev->dev, "Failed to get IRQ.\n"); + goto err; + } + + regulators->irq_ldo_lim = regmap_irq_get_virq(da9063->regmap_irq, irq); + if (regulators->irq_ldo_lim >= 0) { + ret = request_threaded_irq(regulators->irq_ldo_lim, + NULL, da9063_ldo_lim_event, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "LDO_LIM", regulators); + if (ret) { + dev_err(&pdev->dev, + "Failed to request LDO_LIM IRQ.\n"); + regulators->irq_ldo_lim = -ENXIO; + } + } + + return 0; + +err: + /* Wind back regulators registeration */ + while (--n >= 0) + regulator_unregister(regulators->regulator[n].rdev); + + return ret; +} + +static int da9063_regulator_remove(struct platform_device *pdev) +{ + struct da9063_regulators *regulators = platform_get_drvdata(pdev); + struct da9063_regulator *regl; + + free_irq(regulators->irq_ldo_lim, regulators); + free_irq(regulators->irq_uvov, regulators); + + for (regl = ®ulators->regulator[regulators->n_regulators - 1]; + regl >= ®ulators->regulator[0]; regl--) + regulator_unregister(regl->rdev); + + return 0; +} + +static struct platform_driver da9063_regulator_driver = { + .driver = { + .name = DA9063_DRVNAME_REGULATORS, + .owner = THIS_MODULE, + }, + .probe = da9063_regulator_probe, + .remove = da9063_regulator_remove, +}; + +static int __init da9063_regulator_init(void) +{ + return platform_driver_register(&da9063_regulator_driver); +} +subsys_initcall(da9063_regulator_init); + +static void __exit da9063_regulator_cleanup(void) +{ + platform_driver_unregister(&da9063_regulator_driver); +} +module_exit(da9063_regulator_cleanup); + + +/* Module information */ +MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>"); +MODULE_DESCRIPTION("DA9063 regulators driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("paltform:" DA9063_DRVNAME_REGULATORS); diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c new file mode 100644 index 0000000..f0fe54b --- /dev/null +++ b/drivers/regulator/da9210-regulator.c @@ -0,0 +1,196 @@ +/* + * da9210-regulator.c - Regulator device driver for DA9210 + * Copyright (C) 2013 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> + +#include "da9210-regulator.h" + +struct da9210 { + struct regulator_dev *rdev; + struct regmap *regmap; +}; + +static const struct regmap_config da9210_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA); +static int da9210_get_current_limit(struct regulator_dev *rdev); + +static struct regulator_ops da9210_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = da9210_set_current_limit, + .get_current_limit = da9210_get_current_limit, +}; + +/* Default limits measured in millivolts and milliamps */ +#define DA9210_MIN_MV 300 +#define DA9210_MAX_MV 1570 +#define DA9210_STEP_MV 10 + +/* Current limits for buck (uA) indices corresponds with register values */ +static const int da9210_buck_limits[] = { + 1600000, 1800000, 2000000, 2200000, 2400000, 2600000, 2800000, 3000000, + 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 4600000 +}; + +static const struct regulator_desc da9210_reg = { + .name = "DA9210", + .id = 0, + .ops = &da9210_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = ((DA9210_MAX_MV - DA9210_MIN_MV) / DA9210_STEP_MV) + 1, + .min_uV = (DA9210_MIN_MV * 1000), + .uV_step = (DA9210_STEP_MV * 1000), + .vsel_reg = DA9210_REG_VBUCK_A, + .vsel_mask = DA9210_VBUCK_MASK, + .enable_reg = DA9210_REG_BUCK_CONT, + .enable_mask = DA9210_BUCK_EN, + .owner = THIS_MODULE, +}; + +static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct da9210 *chip = rdev_get_drvdata(rdev); + unsigned int sel; + int i; + + /* search for closest to maximum */ + for (i = ARRAY_SIZE(da9210_buck_limits)-1; i >= 0; i--) { + if (min_uA <= da9210_buck_limits[i] && + max_uA >= da9210_buck_limits[i]) { + sel = i; + sel = sel << DA9210_BUCK_ILIM_SHIFT; + return regmap_update_bits(chip->regmap, + DA9210_REG_BUCK_ILIM, + DA9210_BUCK_ILIM_MASK, sel); + } + } + + return -EINVAL; +} + +static int da9210_get_current_limit(struct regulator_dev *rdev) +{ + struct da9210 *chip = rdev_get_drvdata(rdev); + unsigned int data; + unsigned int sel; + int ret; + + ret = regmap_read(chip->regmap, DA9210_REG_BUCK_ILIM, &data); + if (ret < 0) + return ret; + + /* select one of 16 values: 0000 (1600mA) to 1111 (4600mA) */ + sel = (data & DA9210_BUCK_ILIM_MASK) >> DA9210_BUCK_ILIM_SHIFT; + + return da9210_buck_limits[sel]; +} + +/* + * I2C driver interface functions + */ +static int da9210_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9210 *chip; + struct da9210_pdata *pdata = i2c->dev.platform_data; + struct regulator_dev *rdev = NULL; + struct regulator_config config = { }; + int error; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9210), GFP_KERNEL); + if (NULL == chip) { + dev_err(&i2c->dev, + "Cannot kzalloc memory for regulator structure\n"); + return -ENOMEM; + } + + chip->regmap = devm_regmap_init_i2c(i2c, &da9210_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + config.dev = &i2c->dev; + if (pdata) + config.init_data = &pdata->da9210_constraints; + config.driver_data = chip; + config.regmap = chip->regmap; + + rdev = regulator_register(&da9210_reg, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register DA9210 regulator\n"); + return PTR_ERR(rdev); + } + + chip->rdev = rdev; + + i2c_set_clientdata(i2c, chip); + + return 0; +} + +static int da9210_i2c_remove(struct i2c_client *i2c) +{ + struct da9210 *chip = i2c_get_clientdata(i2c); + regulator_unregister(chip->rdev); + return 0; +} + +static const struct i2c_device_id da9210_i2c_id[] = { + {"da9210", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, da9210_i2c_id); + +static struct i2c_driver da9210_regulator_driver = { + .driver = { + .name = "da9210", + .owner = THIS_MODULE, + }, + .probe = da9210_i2c_probe, + .remove = da9210_i2c_remove, + .id_table = da9210_i2c_id, +}; + +module_i2c_driver(da9210_regulator_driver); + +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Regulator device driver for Dialog DA9210"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/da9210-regulator.h b/drivers/regulator/da9210-regulator.h new file mode 100644 index 0000000..749c550 --- /dev/null +++ b/drivers/regulator/da9210-regulator.h @@ -0,0 +1,288 @@ + +/* + * da9210-regulator.h - Regulator definitions for DA9210 + * Copyright (C) 2013 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __DA9210_REGISTERS_H__ +#define __DA9210_REGISTERS_H__ + +struct da9210_pdata { + struct regulator_init_data da9210_constraints; +}; + +/* Page selection */ +#define DA9210_REG_PAGE_CON 0x00 + +/* System Control and Event Registers */ +#define DA9210_REG_STATUS_A 0x50 +#define DA9210_REG_STATUS_B 0x51 +#define DA9210_REG_EVENT_A 0x52 +#define DA9210_REG_EVENT_B 0x53 +#define DA9210_REG_MASK_A 0x54 +#define DA9210_REG_MASK_B 0x55 +#define DA9210_REG_CONTROL_A 0x56 + +/* GPIO Control Registers */ +#define DA9210_REG_GPIO_0_1 0x58 +#define DA9210_REG_GPIO_2_3 0x59 +#define DA9210_REG_GPIO_4_5 0x5A +#define DA9210_REG_GPIO_6 0x5B + +/* Regulator Registers */ +#define DA9210_REG_BUCK_CONT 0x5D +#define DA9210_REG_BUCK_ILIM 0xD0 +#define DA9210_REG_BUCK_CONF1 0xD1 +#define DA9210_REG_BUCK_CONF2 0xD2 +#define DA9210_REG_VBACK_AUTO 0xD4 +#define DA9210_REG_VBACK_BASE 0xD5 +#define DA9210_REG_VBACK_MAX_DVC_IF 0xD6 +#define DA9210_REG_VBACK_DVC 0xD7 +#define DA9210_REG_VBUCK_A 0xD8 +#define DA9210_REG_VBUCK_B 0xD9 + +/* I2C Interface Settings */ +#define DA9210_REG_INTERFACE 0x105 + +/* OTP */ +#define DA9210_REG_OPT_COUNT 0x140 +#define DA9210_REG_OPT_ADDR 0x141 +#define DA9210_REG_OPT_DATA 0x142 + +/* Customer Trim and Configuration */ +#define DA9210_REG_CONFIG_A 0x143 +#define DA9210_REG_CONFIG_B 0x144 +#define DA9210_REG_CONFIG_C 0x145 +#define DA9210_REG_CONFIG_D 0x146 +#define DA9210_REG_CONFIG_E 0x147 + + +/* + * Registers bits + */ +/* DA9210_REG_PAGE_CON (addr=0x00) */ +#define DA9210_PEG_PAGE_SHIFT 0 +#define DA9210_REG_PAGE_MASK 0x0F +/* On I2C registers 0x00 - 0xFF */ +#define DA9210_REG_PAGE0 0 +/* On I2C registers 0x100 - 0x1FF */ +#define DA9210_REG_PAGE2 2 +#define DA9210_PAGE_WRITE_MODE 0x00 +#define DA9210_REPEAT_WRITE_MODE 0x40 +#define DA9210_PAGE_REVERT 0x80 + +/* DA9210_REG_STATUS_A (addr=0x50) */ +#define DA9210_GPI0 0x01 +#define DA9210_GPI1 0x02 +#define DA9210_GPI2 0x04 +#define DA9210_GPI3 0x08 +#define DA9210_GPI4 0x10 +#define DA9210_GPI5 0x20 +#define DA9210_GPI6 0x40 + +/* DA9210_REG_EVENT_A (addr=0x52) */ +#define DA9210_E_GPI0 0x01 +#define DA9210_E_GPI1 0x02 +#define DA9210_E_GPI2 0x04 +#define DA9210_E_GPI3 0x08 +#define DA9210_E_GPI4 0x10 +#define DA9210_E_GPI5 0x20 +#define DA9210_E_GPI6 0x40 + +/* DA9210_REG_EVENT_B (addr=0x53) */ +#define DA9210_E_OVCURR 0x01 +#define DA9210_E_NPWRGOOD 0x02 +#define DA9210_E_TEMP_WARN 0x04 +#define DA9210_E_TEMP_CRIT 0x08 +#define DA9210_E_VMAX 0x10 + +/* DA9210_REG_MASK_A (addr=0x54) */ +#define DA9210_M_GPI0 0x01 +#define DA9210_M_GPI1 0x02 +#define DA9210_M_GPI2 0x04 +#define DA9210_M_GPI3 0x08 +#define DA9210_M_GPI4 0x10 +#define DA9210_M_GPI5 0x20 +#define DA9210_M_GPI6 0x40 + +/* DA9210_REG_MASK_B (addr=0x55) */ +#define DA9210_M_OVCURR 0x01 +#define DA9210_M_NPWRGOOD 0x02 +#define DA9210_M_TEMP_WARN 0x04 +#define DA9210_M_TEMP_CRIT 0x08 +#define DA9210_M_VMAX 0x10 + +/* DA9210_REG_CONTROL_A (addr=0x56) */ +#define DA9210_DEBOUNCING_SHIFT 0 +#define DA9210_DEBOUNCING_MASK 0x07 +#define DA9210_SLEW_RATE_SHIFT 3 +#define DA9210_SLEW_RATE_MASK 0x18 +#define DA9210_V_LOCK 0x20 + +/* DA9210_REG_GPIO_0_1 (addr=0x58) */ +#define DA9210_GPIO0_PIN_SHIFT 0 +#define DA9210_GPIO0_PIN_MASK 0x03 +#define DA9210_GPIO0_PIN_GPI 0x00 +#define DA9210_GPIO0_PIN_GPO_OD 0x02 +#define DA9210_GPIO0_PIN_GPO 0x03 +#define DA9210_GPIO0_TYPE 0x04 +#define DA9210_GPIO0_TYPE_GPI 0x00 +#define DA9210_GPIO0_TYPE_GPO 0x04 +#define DA9210_GPIO0_MODE 0x08 +#define DA9210_GPIO1_PIN_SHIFT 4 +#define DA9210_GPIO1_PIN_MASK 0x30 +#define DA9210_GPIO1_PIN_GPI 0x00 +#define DA9210_GPIO1_PIN_VERROR 0x10 +#define DA9210_GPIO1_PIN_GPO_OD 0x20 +#define DA9210_GPIO1_PIN_GPO 0x30 +#define DA9210_GPIO1_TYPE_SHIFT 0x40 +#define DA9210_GPIO1_TYPE_GPI 0x00 +#define DA9210_GPIO1_TYPE_GPO 0x40 +#define DA9210_GPIO1_MODE 0x80 + +/* DA9210_REG_GPIO_2_3 (addr=0x59) */ +#define DA9210_GPIO2_PIN_SHIFT 0 +#define DA9210_GPIO2_PIN_MASK 0x03 +#define DA9210_GPIO2_PIN_GPI 0x00 +#define DA9210_GPIO5_PIN_BUCK_CLK 0x10 +#define DA9210_GPIO2_PIN_GPO_OD 0x02 +#define DA9210_GPIO2_PIN_GPO 0x03 +#define DA9210_GPIO2_TYPE 0x04 +#define DA9210_GPIO2_TYPE_GPI 0x00 +#define DA9210_GPIO2_TYPE_GPO 0x04 +#define DA9210_GPIO2_MODE 0x08 +#define DA9210_GPIO3_PIN_SHIFT 4 +#define DA9210_GPIO3_PIN_MASK 0x30 +#define DA9210_GPIO3_PIN_GPI 0x00 +#define DA9210_GPIO3_PIN_IERROR 0x10 +#define DA9210_GPIO3_PIN_GPO_OD 0x20 +#define DA9210_GPIO3_PIN_GPO 0x30 +#define DA9210_GPIO3_TYPE_SHIFT 0x40 +#define DA9210_GPIO3_TYPE_GPI 0x00 +#define DA9210_GPIO3_TYPE_GPO 0x40 +#define DA9210_GPIO3_MODE 0x80 + +/* DA9210_REG_GPIO_4_5 (addr=0x5A) */ +#define DA9210_GPIO4_PIN_SHIFT 0 +#define DA9210_GPIO4_PIN_MASK 0x03 +#define DA9210_GPIO4_PIN_GPI 0x00 +#define DA9210_GPIO4_PIN_GPO_OD 0x02 +#define DA9210_GPIO4_PIN_GPO 0x03 +#define DA9210_GPIO4_TYPE 0x04 +#define DA9210_GPIO4_TYPE_GPI 0x00 +#define DA9210_GPIO4_TYPE_GPO 0x04 +#define DA9210_GPIO4_MODE 0x08 +#define DA9210_GPIO5_PIN_SHIFT 4 +#define DA9210_GPIO5_PIN_MASK 0x30 +#define DA9210_GPIO5_PIN_GPI 0x00 +#define DA9210_GPIO5_PIN_INTERFACE 0x01 +#define DA9210_GPIO5_PIN_GPO_OD 0x20 +#define DA9210_GPIO5_PIN_GPO 0x30 +#define DA9210_GPIO5_TYPE_SHIFT 0x40 +#define DA9210_GPIO5_TYPE_GPI 0x00 +#define DA9210_GPIO5_TYPE_GPO 0x40 +#define DA9210_GPIO5_MODE 0x80 + +/* DA9210_REG_GPIO_6 (addr=0x5B) */ +#define DA9210_GPIO6_PIN_SHIFT 0 +#define DA9210_GPIO6_PIN_MASK 0x03 +#define DA9210_GPIO6_PIN_GPI 0x00 +#define DA9210_GPIO6_PIN_INTERFACE 0x01 +#define DA9210_GPIO6_PIN_GPO_OD 0x02 +#define DA9210_GPIO6_PIN_GPO 0x03 +#define DA9210_GPIO6_TYPE 0x04 +#define DA9210_GPIO6_TYPE_GPI 0x00 +#define DA9210_GPIO6_TYPE_GPO 0x04 +#define DA9210_GPIO6_MODE 0x08 + +/* DA9210_REG_BUCK_CONT (addr=0x5D) */ +#define DA9210_BUCK_EN 0x01 +#define DA9210_BUCK_GPI_SHIFT 1 +#define DA9210_BUCK_GPI_MASK 0x06 +#define DA9210_BUCK_GPI_OFF 0x00 +#define DA9210_BUCK_GPI_GPIO0 0x02 +#define DA9210_BUCK_GPI_GPIO3 0x04 +#define DA9210_BUCK_GPI_GPIO4 0x06 +#define DA9210_BUCK_PD_DIS 0x08 +#define DA9210_VBUCK_SEL 0x10 +#define DA9210_VBUCK_SEL_A 0x00 +#define DA9210_VBUCK_SEL_B 0x10 +#define DA9210_VBUCK_GPI_SHIFT 5 +#define DA9210_VBUCK_GPI_MASK 0x60 +#define DA9210_VBUCK_GPI_OFF 0x00 +#define DA9210_VBUCK_GPI_GPIO0 0x20 +#define DA9210_VBUCK_GPI_GPIO3 0x40 +#define DA9210_VBUCK_GPI_GPIO4 0x60 +#define DA9210_DVC_CTRL_EN 0x80 + +/* DA9210_REG_BUCK_ILIM (addr=0xD0) */ +#define DA9210_BUCK_ILIM_SHIFT 0 +#define DA9210_BUCK_ILIM_MASK 0x0F +#define DA9210_BUCK_IALARM 0x10 + +/* DA9210_REG_BUCK_CONF1 (addr=0xD1) */ +#define DA9210_BUCK_MODE_SHIFT 0 +#define DA9210_BUCK_MODE_MASK 0x03 +#define DA9210_BUCK_MODE_MANUAL 0x00 +#define DA9210_BUCK_MODE_SLEEP 0x01 +#define DA9210_BUCK_MODE_SYNC 0x02 +#define DA9210_BUCK_MODE_AUTO 0x03 +#define DA9210_STARTUP_CTRL_SHIFT 2 +#define DA9210_STARTUP_CTRL_MASK 0x1C +#define DA9210_PWR_DOWN_CTRL_SHIFT 5 +#define DA9210_PWR_DOWN_CTRL_MASK 0xE0 + +/* DA9210_REG_BUCK_CONF2 (addr=0xD2) */ +#define DA9210_PHASE_SEL_SHIFT 0 +#define DA9210_PHASE_SEL_MASK 0x03 +#define DA9210_FREQ_SEL 0x40 + +/* DA9210_REG_BUCK_AUTO (addr=0xD4) */ +#define DA9210_VBUCK_AUTO_SHIFT 0 +#define DA9210_VBUCK_AUTO_MASK 0x7F + +/* DA9210_REG_BUCK_BASE (addr=0xD5) */ +#define DA9210_VBUCK_BASE_SHIFT 0 +#define DA9210_VBUCK_BASE_MASK 0x7F + +/* DA9210_REG_VBUCK_MAX_DVC_IF (addr=0xD6) */ +#define DA9210_VBUCK_MAX_SHIFT 0 +#define DA9210_VBUCK_MAX_MASK 0x7F +#define DA9210_DVC_STEP_SIZE 0x80 +#define DA9210_DVC_STEP_SIZE_10MV 0x00 +#define DA9210_DVC_STEP_SIZE_20MV 0x80 + +/* DA9210_REG_VBUCK_DVC (addr=0xD7) */ +#define DA9210_VBUCK_DVC_SHIFT 0 +#define DA9210_VBUCK_DVC_MASK 0x7F + +/* DA9210_REG_VBUCK_A/B (addr=0xD8/0xD9) */ +#define DA9210_VBUCK_SHIFT 0 +#define DA9210_VBUCK_MASK 0x7F +#define DA9210_VBUCK_BIAS 0 +#define DA9210_BUCK_SL 0x80 + +/* DA9210_REG_INTERFACE (addr=0x105) */ +#define DA9210_IF_BASE_ADDR_SHIFT 4 +#define DA9210_IF_BASE_ADDR_MASK 0xF0 + +/* DA9210_REG_CONFIG_E (addr=0x147) */ +#define DA9210_STAND_ALONE 0x01 + +#endif /* __DA9210_REGISTERS_H__ */ + diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index f0e1ae5..a32b442 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -237,7 +237,7 @@ static int fan53555_regulator_probe(struct i2c_client *client, unsigned int val; int ret; - pdata = client->dev.platform_data; + pdata = dev_get_platdata(&client->dev); if (!pdata || !pdata->regulator) { dev_err(&client->dev, "Platform data not found!\n"); return -ENODEV; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index e5c03b5..7610920 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -146,7 +146,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) if (IS_ERR(config)) return PTR_ERR(config); } else { - config = pdev->dev.platform_data; + config = dev_get_platdata(&pdev->dev); } if (!config) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 9d39eb4..98a98ff 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -219,7 +219,7 @@ static struct regulator_ops gpio_regulator_current_ops = { static int gpio_regulator_probe(struct platform_device *pdev) { - struct gpio_regulator_config *config = pdev->dev.platform_data; + struct gpio_regulator_config *config = dev_get_platdata(&pdev->dev); struct device_node *np = pdev->dev.of_node; struct gpio_regulator_data *drvdata; struct regulator_config cfg = { }; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c new file mode 100644 index 0000000..6e30df1 --- /dev/null +++ b/drivers/regulator/helpers.c @@ -0,0 +1,447 @@ +/* + * helpers.c -- Voltage/Current Regulator framework helper functions. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * Copyright 2008 SlimLogic Ltd. + * + * 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/err.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/module.h> + +/** + * regulator_is_enabled_regmap - standard is_enabled() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their is_enabled operation, saving some code. + */ +int regulator_is_enabled_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret != 0) + return ret; + + if (rdev->desc->enable_is_inverted) + return (val & rdev->desc->enable_mask) == 0; + else + return (val & rdev->desc->enable_mask) != 0; +} +EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); + +/** + * regulator_enable_regmap - standard enable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their enable() operation, saving some code. + */ +int regulator_enable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = 0; + else + val = rdev->desc->enable_mask; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_enable_regmap); + +/** + * regulator_disable_regmap - standard disable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their disable() operation, saving some code. + */ +int regulator_disable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = rdev->desc->enable_mask; + else + val = 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_disable_regmap); + +/** + * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their get_voltage_vsel operation, saving some code. + */ +int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap); + +/** + * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * @sel: Selector to set + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their set_voltage_vsel operation, saving some code. + */ +int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel) +{ + int ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + if (rdev->desc->apply_bit) + ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap); + +/** + * regulator_map_voltage_iterate - map_voltage() based on list_voltage() + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers implementing set_voltage_sel() and list_voltage() can use + * this as their map_voltage() operation. It will find a suitable + * voltage by calling list_voltage() until it gets something in bounds + * for the requested voltages. + */ +int regulator_map_voltage_iterate(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int best_val = INT_MAX; + int selector = 0; + int i, ret; + + /* Find the smallest voltage that falls within the specified + * range. + */ + for (i = 0; i < rdev->desc->n_voltages; i++) { + ret = rdev->desc->ops->list_voltage(rdev, i); + if (ret < 0) + continue; + + if (ret < best_val && ret >= min_uV && ret <= max_uV) { + best_val = ret; + selector = i; + } + } + + if (best_val != INT_MAX) + return selector; + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate); + +/** + * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers that have ascendant voltage list can use this as their + * map_voltage() operation. + */ +int regulator_map_voltage_ascend(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int i, ret; + + for (i = 0; i < rdev->desc->n_voltages; i++) { + ret = rdev->desc->ops->list_voltage(rdev, i); + if (ret < 0) + continue; + + if (ret > max_uV) + break; + + if (ret >= min_uV && ret <= max_uV) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend); + +/** + * regulator_map_voltage_linear - map_voltage() for simple linear mappings + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers providing min_uV and uV_step in their regulator_desc can + * use this as their map_voltage() operation. + */ +int regulator_map_voltage_linear(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret, voltage; + + /* Allow uV_step to be 0 for fixed voltage */ + if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) { + if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV) + return 0; + else + return -EINVAL; + } + + if (!rdev->desc->uV_step) { + BUG_ON(!rdev->desc->uV_step); + return -EINVAL; + } + + if (min_uV < rdev->desc->min_uV) + min_uV = rdev->desc->min_uV; + + ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step); + if (ret < 0) + return ret; + + ret += rdev->desc->linear_min_sel; + + /* Map back into a voltage to verify we're still in bounds */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_linear); + +/** + * regulator_map_voltage_linear - map_voltage() for multiple linear ranges + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers providing linear_ranges in their descriptor can use this as + * their map_voltage() callback. + */ +int regulator_map_voltage_linear_range(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + const struct regulator_linear_range *range; + int ret = -EINVAL; + int voltage, i; + + if (!rdev->desc->n_linear_ranges) { + BUG_ON(!rdev->desc->n_linear_ranges); + return -EINVAL; + } + + for (i = 0; i < rdev->desc->n_linear_ranges; i++) { + range = &rdev->desc->linear_ranges[i]; + + if (!(min_uV <= range->max_uV && max_uV >= range->min_uV)) + continue; + + if (min_uV <= range->min_uV) + min_uV = range->min_uV; + + /* range->uV_step == 0 means fixed voltage range */ + if (range->uV_step == 0) { + ret = 0; + } else { + ret = DIV_ROUND_UP(min_uV - range->min_uV, + range->uV_step); + if (ret < 0) + return ret; + } + + ret += range->min_sel; + + break; + } + + if (i == rdev->desc->n_linear_ranges) + return -EINVAL; + + /* Map back into a voltage to verify we're still in bounds */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); + +/** + * regulator_list_voltage_linear - List voltages with simple calculation + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with a simple linear mapping between voltages and + * selectors can set min_uV and uV_step in the regulator descriptor + * and then use this function as their list_voltage() operation, + */ +int regulator_list_voltage_linear(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; + + selector -= rdev->desc->linear_min_sel; + + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); + +/** + * regulator_list_voltage_linear_range - List voltages for linear ranges + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with a series of simple linear mappings between voltages + * and selectors can set linear_ranges in the regulator descriptor and + * then use this function as their list_voltage() operation, + */ +int regulator_list_voltage_linear_range(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct regulator_linear_range *range; + int i; + + if (!rdev->desc->n_linear_ranges) { + BUG_ON(!rdev->desc->n_linear_ranges); + return -EINVAL; + } + + for (i = 0; i < rdev->desc->n_linear_ranges; i++) { + range = &rdev->desc->linear_ranges[i]; + + if (!(selector >= range->min_sel && + selector <= range->max_sel)) + continue; + + selector -= range->min_sel; + + return range->min_uV + (range->uV_step * selector); + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_linear_range); + +/** + * regulator_list_voltage_table - List voltages with table based mapping + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with table based mapping between voltages and + * selectors can set volt_table in the regulator descriptor + * and then use this function as their list_voltage() operation. + */ +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector) +{ + if (!rdev->desc->volt_table) { + BUG_ON(!rdev->desc->volt_table); + return -EINVAL; + } + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + return rdev->desc->volt_table[selector]; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_table); + +/** + * regulator_set_bypass_regmap - Default set_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: state to set. + */ +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) +{ + unsigned int val; + + if (enable) + val = rdev->desc->bypass_mask; + else + val = 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, + rdev->desc->bypass_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); + +/** + * regulator_get_bypass_regmap - Default get_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: current state. + */ +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); + if (ret != 0) + return ret; + + *enable = val & rdev->desc->bypass_mask; + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index b99c49b..88c1a3a 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -110,7 +110,7 @@ static int isl6271a_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct regulator_config config = { }; - struct regulator_init_data *init_data = i2c->dev.platform_data; + struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev); struct isl_pmic *pmic; int err, i; diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 3809b43..5a4604e 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -425,7 +425,7 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct lp3971 *lp3971; - struct lp3971_platform_data *pdata = i2c->dev.platform_data; + struct lp3971_platform_data *pdata = dev_get_platdata(&i2c->dev); int ret; u16 val; diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index 5730240..093e6f4 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -519,7 +519,7 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct lp3972 *lp3972; - struct lp3972_platform_data *pdata = i2c->dev.platform_data; + struct lp3972_platform_data *pdata = dev_get_platdata(&i2c->dev); int ret; u16 val; diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index b16336b..2b84b72 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -373,7 +373,7 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, return -EINVAL; } - for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) { + for (i = ARRAY_SIZE(lp8725_buck_uA) - 1; i >= 0; i--) { if (lp8725_buck_uA[i] >= min_uA && lp8725_buck_uA[i] <= max_uA) return lp872x_update_bits(lp, addr, @@ -787,7 +787,7 @@ static int lp872x_regulator_register(struct lp872x *lp) struct regulator_dev *rdev; int i, ret; - for (i = 0 ; i < lp->num_regulators ; i++) { + for (i = 0; i < lp->num_regulators; i++) { desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : &lp8725_regulator_desc[i]; @@ -820,7 +820,7 @@ static void lp872x_regulator_unregister(struct lp872x *lp) struct regulator_dev *rdev; int i; - for (i = 0 ; i < lp->num_regulators ; i++) { + for (i = 0; i < lp->num_regulators; i++) { rdev = *(lp->regulators + i); regulator_unregister(rdev); } @@ -907,7 +907,8 @@ static struct lp872x_platform_data goto out; for (i = 0; i < num_matches; i++) { - pdata->regulator_data[i].id = (int)match[i].driver_data; + pdata->regulator_data[i].id = + (enum lp872x_regulator_id)match[i].driver_data; pdata->regulator_data[i].init_data = match[i].init_data; /* Operation mode configuration for buck/buck1/buck2 */ @@ -961,7 +962,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) } lp->dev = &cl->dev; - lp->pdata = cl->dev.platform_data; + lp->pdata = dev_get_platdata(&cl->dev); lp->chipid = id->driver_data; lp->num_regulators = num_regulators; i2c_set_clientdata(cl, lp); diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c index d9e38b4..785a25e 100644 --- a/drivers/regulator/lp8755.c +++ b/drivers/regulator/lp8755.c @@ -228,6 +228,7 @@ err_i2c: } static struct regulator_ops lp8755_buck_ops = { + .map_voltage = regulator_map_voltage_linear, .list_voltage = regulator_list_voltage_linear, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -449,7 +450,7 @@ static int lp8755_probe(struct i2c_client *client, { int ret, icnt; struct lp8755_chip *pchip; - struct lp8755_platform_data *pdata = client->dev.platform_data; + struct lp8755_platform_data *pdata = dev_get_platdata(&client->dev); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "i2c functionality check fail.\n"); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 54af610..3a599ee 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -163,7 +163,7 @@ static int max1586_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct regulator_dev **rdev; - struct max1586_platform_data *pdata = client->dev.platform_data; + struct max1586_platform_data *pdata = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct max1586_data *max1586; int i, id, ret = -ENOMEM; diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index db6c9be..19c6f08 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -152,7 +152,7 @@ static struct regmap_config max8649_regmap_config = { static int max8649_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct max8649_platform_data *pdata = client->dev.platform_data; + struct max8649_platform_data *pdata = dev_get_platdata(&client->dev); struct max8649_regulator_info *info = NULL; struct regulator_config config = { }; unsigned int val; diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index d428ef9..144bcac 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -44,6 +44,9 @@ #include <linux/regulator/driver.h> #include <linux/slab.h> #include <linux/regulator/max8660.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/of_regulator.h> #define MAX8660_DCDC_MIN_UV 725000 #define MAX8660_DCDC_MAX_UV 1800000 @@ -305,21 +308,105 @@ static const struct regulator_desc max8660_reg[] = { }, }; +enum { + MAX8660 = 0, + MAX8661 = 1, +}; + +#ifdef CONFIG_OF +static const struct of_device_id max8660_dt_ids[] = { + { .compatible = "maxim,max8660", .data = (void *) MAX8660 }, + { .compatible = "maxim,max8661", .data = (void *) MAX8661 }, + { } +}; +MODULE_DEVICE_TABLE(of, max8660_dt_ids); + +static int max8660_pdata_from_dt(struct device *dev, + struct device_node **of_node, + struct max8660_platform_data *pdata) +{ + int matched, i; + struct device_node *np; + struct max8660_subdev_data *sub; + struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)]; + + np = of_find_node_by_name(dev->of_node, "regulators"); + if (!np) { + dev_err(dev, "missing 'regulators' subnode in DT\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rmatch); i++) + rmatch[i].name = max8660_reg[i].name; + + matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch)); + if (matched <= 0) + return matched; + + pdata->subdevs = devm_kzalloc(dev, sizeof(struct max8660_subdev_data) * + matched, GFP_KERNEL); + if (!pdata->subdevs) + return -ENOMEM; + + pdata->num_subdevs = matched; + sub = pdata->subdevs; + + for (i = 0; i < matched; i++) { + sub->id = i; + sub->name = rmatch[i].name; + sub->platform_data = rmatch[i].init_data; + of_node[i] = rmatch[i].of_node; + sub++; + } + + return 0; +} +#else +static inline int max8660_pdata_from_dt(struct device *dev, + struct device_node **of_node, + struct max8660_platform_data *pdata) +{ + return 0; +} +#endif + static int max8660_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct regulator_dev **rdev; - struct max8660_platform_data *pdata = client->dev.platform_data; + struct device *dev = &client->dev; + struct max8660_platform_data *pdata = dev_get_platdata(dev); struct regulator_config config = { }; struct max8660 *max8660; int boot_on, i, id, ret = -EINVAL; + struct device_node *of_node[MAX8660_V_END]; + unsigned long type; + + if (dev->of_node && !pdata) { + const struct of_device_id *id; + struct max8660_platform_data pdata_of; + + id = of_match_device(of_match_ptr(max8660_dt_ids), dev); + if (!id) + return -ENODEV; + + ret = max8660_pdata_from_dt(dev, of_node, &pdata_of); + if (ret < 0) + return ret; + + pdata = &pdata_of; + type = (unsigned long) id->data; + } else { + type = i2c_id->driver_data; + memset(of_node, 0, sizeof(of_node)); + } if (pdata->num_subdevs > MAX8660_V_END) { - dev_err(&client->dev, "Too many regulators found!\n"); + dev_err(dev, "Too many regulators found!\n"); return -EINVAL; } - max8660 = devm_kzalloc(&client->dev, sizeof(struct max8660) + + max8660 = devm_kzalloc(dev, sizeof(struct max8660) + sizeof(struct regulator_dev *) * MAX8660_V_END, GFP_KERNEL); if (!max8660) @@ -376,8 +463,8 @@ static int max8660_probe(struct i2c_client *client, break; case MAX8660_V7: - if (!strcmp(i2c_id->name, "max8661")) { - dev_err(&client->dev, "Regulator not on this chip!\n"); + if (type == MAX8661) { + dev_err(dev, "Regulator not on this chip!\n"); goto err_out; } @@ -386,7 +473,7 @@ static int max8660_probe(struct i2c_client *client, break; default: - dev_err(&client->dev, "invalid regulator %s\n", + dev_err(dev, "invalid regulator %s\n", pdata->subdevs[i].name); goto err_out; } @@ -397,14 +484,15 @@ static int max8660_probe(struct i2c_client *client, id = pdata->subdevs[i].id; - config.dev = &client->dev; + config.dev = dev; config.init_data = pdata->subdevs[i].platform_data; + config.of_node = of_node[i]; config.driver_data = max8660; rdev[i] = regulator_register(&max8660_reg[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); - dev_err(&client->dev, "failed to register %s\n", + dev_err(dev, "failed to register %s\n", max8660_reg[id].name); goto err_unregister; } @@ -431,8 +519,8 @@ static int max8660_remove(struct i2c_client *client) } static const struct i2c_device_id max8660_id[] = { - { "max8660", 0 }, - { "max8661", 0 }, + { .name = "max8660", .driver_data = MAX8660 }, + { .name = "max8661", .driver_data = MAX8661 }, { } }; MODULE_DEVICE_TABLE(i2c, max8660_id); diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index e6d54a5..d80b5fa 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -277,7 +277,7 @@ static int max8925_regulator_dt_init(struct platform_device *pdev, static int max8925_regulator_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct regulator_init_data *pdata = pdev->dev.platform_data; + struct regulator_init_data *pdata = dev_get_platdata(&pdev->dev); struct regulator_config config = { }; struct max8925_regulator_info *ri; struct resource *res; diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 5259c2f..788e5ae 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -196,7 +196,7 @@ static int max8952_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct max8952_platform_data *pdata = client->dev.platform_data; + struct max8952_platform_data *pdata = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct max8952_data *max8952; diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 0c5195a..5b77ab7 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -371,7 +371,7 @@ static int max8973_probe(struct i2c_client *client, struct max8973_chip *max; int ret; - pdata = client->dev.platform_data; + pdata = dev_get_platdata(&client->dev); if (!pdata && !client->dev.of_node) { dev_err(&client->dev, "No Platform data"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f3c8f8f..7827384 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -21,6 +21,7 @@ static void of_get_regulation_constraints(struct device_node *np, { const __be32 *min_uV, *max_uV, *uV_offset; const __be32 *min_uA, *max_uA, *ramp_delay; + struct property *prop; struct regulation_constraints *constraints = &(*init_data)->constraints; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -64,9 +65,14 @@ static void of_get_regulation_constraints(struct device_node *np, if (of_property_read_bool(np, "regulator-allow-bypass")) constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; - ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); - if (ramp_delay) - constraints->ramp_delay = be32_to_cpu(*ramp_delay); + prop = of_find_property(np, "regulator-ramp-delay", NULL); + if (prop && prop->value) { + ramp_delay = prop->value; + if (*ramp_delay) + constraints->ramp_delay = be32_to_cpu(*ramp_delay); + else + constraints->ramp_disable = true; + } } /** diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index d0c8785..488dfe7 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -97,11 +97,16 @@ static const struct regs_info palmas_regs_info[] = { .ctrl_addr = PALMAS_SMPS9_CTRL, }, { - .name = "SMPS10", + .name = "SMPS10_OUT2", .sname = "smps10-in", .ctrl_addr = PALMAS_SMPS10_CTRL, }, { + .name = "SMPS10_OUT1", + .sname = "smps10-out2", + .ctrl_addr = PALMAS_SMPS10_CTRL, + }, + { .name = "LDO1", .sname = "ldo1-in", .vsel_addr = PALMAS_LDO1_VOLTAGE, @@ -487,6 +492,8 @@ static struct regulator_ops palmas_ops_smps10 = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, }; static int palmas_is_enabled_ldo(struct regulator_dev *dev) @@ -538,7 +545,8 @@ static int palmas_smps_init(struct palmas *palmas, int id, return ret; switch (id) { - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK; if (reg_init->mode_sleep) reg |= reg_init->mode_sleep << @@ -681,7 +689,8 @@ static struct of_regulator_match palmas_matches[] = { { .name = "smps7", }, { .name = "smps8", }, { .name = "smps9", }, - { .name = "smps10", }, + { .name = "smps10_out2", }, + { .name = "smps10_out1", }, { .name = "ldo1", }, { .name = "ldo2", }, { .name = "ldo3", }, @@ -765,7 +774,7 @@ static void palmas_dt_to_pdata(struct device *dev, static int palmas_regulators_probe(struct platform_device *pdev) { struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); - struct palmas_pmic_platform_data *pdata = pdev->dev.platform_data; + struct palmas_pmic_platform_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct regulator_dev *rdev; struct regulator_config config = { }; @@ -838,7 +847,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) continue; ramp_delay_support = true; break; - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: if (!PALMAS_PMIC_HAS(palmas, SMPS10_BOOST)) continue; } @@ -872,7 +882,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].id = id; switch (id) { - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES; pmic->desc[id].ops = &palmas_ops_smps10; pmic->desc[id].vsel_reg = @@ -882,7 +893,14 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, PALMAS_SMPS10_CTRL); - pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + if (id == PALMAS_REG_SMPS10_OUT1) + pmic->desc[id].enable_mask = SMPS10_SWITCH_EN; + else + pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + pmic->desc[id].bypass_reg = + PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, + PALMAS_SMPS10_CTRL); + pmic->desc[id].bypass_mask = SMPS10_BYPASS_EN; pmic->desc[id].min_uV = 3750000; pmic->desc[id].uV_step = 1250000; break; diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 1a73a29..b49eaee 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -243,7 +243,7 @@ static int pcap_regulator_probe(struct platform_device *pdev) struct regulator_config config = { }; config.dev = &pdev->dev; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = pcap; rdev = regulator_register(&pcap_regulators[pdev->id], &config); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 54df9f7..0f3576d 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -86,7 +86,7 @@ static int pcf50633_regulator_probe(struct platform_device *pdev) pcf = dev_to_pcf50633(pdev->dev.parent); config.dev = &pdev->dev; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = pcf; config.regmap = pcf->regmap; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c new file mode 100644 index 0000000..ba67b2c --- /dev/null +++ b/drivers/regulator/pfuze100-regulator.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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/init.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/of_regulator.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/pfuze100.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +#define PFUZE_NUMREGS 128 +#define PFUZE100_VOL_OFFSET 0 +#define PFUZE100_STANDBY_OFFSET 1 +#define PFUZE100_MODE_OFFSET 3 +#define PFUZE100_CONF_OFFSET 4 + +#define PFUZE100_DEVICEID 0x0 +#define PFUZE100_REVID 0x3 +#define PFUZE100_FABID 0x3 + +#define PFUZE100_SW1ABVOL 0x20 +#define PFUZE100_SW1CVOL 0x2e +#define PFUZE100_SW2VOL 0x35 +#define PFUZE100_SW3AVOL 0x3c +#define PFUZE100_SW3BVOL 0x43 +#define PFUZE100_SW4VOL 0x4a +#define PFUZE100_SWBSTCON1 0x66 +#define PFUZE100_VREFDDRCON 0x6a +#define PFUZE100_VSNVSVOL 0x6b +#define PFUZE100_VGEN1VOL 0x6c +#define PFUZE100_VGEN2VOL 0x6d +#define PFUZE100_VGEN3VOL 0x6e +#define PFUZE100_VGEN4VOL 0x6f +#define PFUZE100_VGEN5VOL 0x70 +#define PFUZE100_VGEN6VOL 0x71 + +struct pfuze_regulator { + struct regulator_desc desc; + unsigned char stby_reg; + unsigned char stby_mask; +}; + +struct pfuze_chip { + struct regmap *regmap; + struct device *dev; + struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; + struct regulator_dev *regulators[PFUZE100_MAX_REGULATOR]; +}; + +static const int pfuze100_swbst[] = { + 5000000, 5050000, 5100000, 5150000, +}; + +static const int pfuze100_vsnvs[] = { + 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, +}; + +static const struct i2c_device_id pfuze_device_id[] = { + {.name = "pfuze100"}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pfuze_device_id); + +static const struct of_device_id pfuze_dt_ids[] = { + { .compatible = "fsl,pfuze100" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pfuze_dt_ids); + +static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); + int id = rdev->desc->id; + unsigned int ramp_bits; + int ret; + + if (id < PFUZE100_SWBST) { + ramp_delay = 12500 / ramp_delay; + ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3); + ret = regmap_update_bits(pfuze100->regmap, + rdev->desc->vsel_reg + 4, + 0xc0, ramp_bits << 6); + if (ret < 0) + dev_err(pfuze100->dev, "ramp failed, err %d\n", ret); + } else + ret = -EACCES; + + return ret; +} + +static struct regulator_ops pfuze100_ldo_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops pfuze100_fixed_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops pfuze100_sw_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pfuze100_set_ramp_delay, +}; + +static struct regulator_ops pfuze100_swb_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + +}; + +#define PFUZE100_FIXED_REG(_name, base, voltage) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = 1, \ + .ops = &pfuze100_fixed_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + } + +#define PFUZE100_SW_REG(_name, base, min, max, step) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x3f, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0x3f, \ + } + +#define PFUZE100_SWB_REG(_name, base, mask, voltages) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pfuze100_swb_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + }, \ + } + +#define PFUZE100_VGEN_REG(_name, base, min, max, step) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0xf, \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ + } + +static struct pfuze_regulator pfuze100_regulators[] = { + PFUZE100_SW_REG(SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000), + PFUZE100_SWB_REG(SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst), + PFUZE100_SWB_REG(VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + +#ifdef CONFIG_OF +static struct of_regulator_match pfuze100_matches[] = { + { .name = "sw1ab", }, + { .name = "sw1c", }, + { .name = "sw2", }, + { .name = "sw3a", }, + { .name = "sw3b", }, + { .name = "sw4", }, + { .name = "swbst", }, + { .name = "vsnvs", }, + { .name = "vrefddr", }, + { .name = "vgen1", }, + { .name = "vgen2", }, + { .name = "vgen3", }, + { .name = "vgen4", }, + { .name = "vgen5", }, + { .name = "vgen6", }, +}; + +static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) +{ + struct device *dev = chip->dev; + struct device_node *np, *parent; + int ret; + + np = of_node_get(dev->parent->of_node); + if (!np) + return 0; + + parent = of_find_node_by_name(np, "regulators"); + if (!parent) { + dev_err(dev, "regulators node not found\n"); + return -EINVAL; + } + + ret = of_regulator_match(dev, parent, pfuze100_matches, + ARRAY_SIZE(pfuze100_matches)); + + of_node_put(parent); + if (ret < 0) { + dev_err(dev, "Error parsing regulator init data: %d\n", + ret); + return ret; + } + + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return pfuze100_matches[index].init_data; +} + +static inline struct device_node *match_of_node(int index) +{ + return pfuze100_matches[index].of_node; +} +#else +static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) +{ + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return NULL; +} + +static inline struct device_node *match_of_node(int index) +{ + return NULL; +} +#endif + +static int pfuze_identify(struct pfuze_chip *pfuze_chip) +{ + unsigned int value; + int ret; + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_DEVICEID, &value); + if (ret) + return ret; + + if (value & 0x0f) { + dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); + return -ENODEV; + } + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_REVID, &value); + if (ret) + return ret; + dev_info(pfuze_chip->dev, + "Full lay: %x, Metal lay: %x\n", + (value & 0xf0) >> 4, value & 0x0f); + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_FABID, &value); + if (ret) + return ret; + dev_info(pfuze_chip->dev, "FAB: %x, FIN: %x\n", + (value & 0xc) >> 2, value & 0x3); + + return 0; +} + +static const struct regmap_config pfuze_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PFUZE_NUMREGS - 1, + .cache_type = REGCACHE_RBTREE, +}; + +static int pfuze100_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pfuze_chip *pfuze_chip; + struct pfuze_regulator_platform_data *pdata = + dev_get_platdata(&client->dev); + struct regulator_config config = { }; + int i, ret; + + pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip), + GFP_KERNEL); + if (!pfuze_chip) + return -ENOMEM; + + i2c_set_clientdata(client, pfuze_chip); + + memcpy(pfuze_chip->regulator_descs, pfuze100_regulators, + sizeof(pfuze_chip->regulator_descs)); + + pfuze_chip->dev = &client->dev; + + pfuze_chip->regmap = devm_regmap_init_i2c(client, &pfuze_regmap_config); + if (IS_ERR(pfuze_chip->regmap)) { + ret = PTR_ERR(pfuze_chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pfuze_identify(pfuze_chip); + if (ret) { + dev_err(&client->dev, "unrecognized pfuze chip ID!\n"); + return ret; + } + + ret = pfuze_parse_regulators_dt(pfuze_chip); + if (ret) + return ret; + + for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) { + struct regulator_init_data *init_data; + struct regulator_desc *desc; + int val; + + desc = &pfuze_chip->regulator_descs[i].desc; + + if (pdata) + init_data = pdata->init_data[i]; + else + init_data = match_init_data(i); + + /* SW2~SW4 high bit check and modify the voltage value table */ + if (i > PFUZE100_SW1C && i < PFUZE100_SWBST) { + regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); + if (val & 0x40) { + desc->min_uV = 800000; + desc->uV_step = 50000; + desc->n_voltages = 51; + } + } + + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = pfuze_chip; + config.of_node = match_of_node(i); + + pfuze_chip->regulators[i] = regulator_register(desc, &config); + if (IS_ERR(pfuze_chip->regulators[i])) { + dev_err(&client->dev, "register regulator%s failed\n", + pfuze100_regulators[i].desc.name); + ret = PTR_ERR(pfuze_chip->regulators[i]); + while (--i >= 0) + regulator_unregister(pfuze_chip->regulators[i]); + return ret; + } + } + + return 0; +} + +static int pfuze100_regulator_remove(struct i2c_client *client) +{ + int i; + struct pfuze_chip *pfuze_chip = i2c_get_clientdata(client); + + for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) + regulator_unregister(pfuze_chip->regulators[i]); + + return 0; +} + +static struct i2c_driver pfuze_driver = { + .id_table = pfuze_device_id, + .driver = { + .name = "pfuze100-regulator", + .owner = THIS_MODULE, + .of_match_table = pfuze_dt_ids, + }, + .probe = pfuze100_regulator_probe, + .remove = pfuze100_regulator_remove, +}; +module_i2c_driver(pfuze_driver); + +MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); +MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100 PMIC"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:pfuze100-regulator"); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 2f62564..5eba2ff 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -16,12 +16,17 @@ #include <linux/gpio.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> +#define S2MPS11_REGULATOR_CNT ARRAY_SIZE(regulators) + struct s2mps11_info { struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX]; @@ -31,11 +36,6 @@ struct s2mps11_info { int ramp_delay16; int ramp_delay7810; int ramp_delay9; - - bool buck6_ramp; - bool buck2_ramp; - bool buck3_ramp; - bool buck4_ramp; }; static int get_ramp_delay(int ramp_delay) @@ -50,9 +50,171 @@ static int get_ramp_delay(int ramp_delay) break; cnt++; } + + if (cnt > 3) + cnt = 3; + return cnt; } +static int s2mps11_regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + unsigned int ramp_delay = 0; + int old_volt, new_volt; + + switch (rdev->desc->id) { + case S2MPS11_BUCK2: + ramp_delay = s2mps11->ramp_delay2; + break; + case S2MPS11_BUCK3: + ramp_delay = s2mps11->ramp_delay34; + break; + case S2MPS11_BUCK4: + ramp_delay = s2mps11->ramp_delay34; + break; + case S2MPS11_BUCK5: + ramp_delay = s2mps11->ramp_delay5; + break; + case S2MPS11_BUCK6: + case S2MPS11_BUCK1: + ramp_delay = s2mps11->ramp_delay16; + break; + case S2MPS11_BUCK7: + case S2MPS11_BUCK8: + case S2MPS11_BUCK10: + ramp_delay = s2mps11->ramp_delay7810; + break; + case S2MPS11_BUCK9: + ramp_delay = s2mps11->ramp_delay9; + } + + if (ramp_delay == 0) + ramp_delay = rdev->desc->ramp_delay; + + old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector); + new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector); + + return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); +} + +static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + unsigned int ramp_val, ramp_shift, ramp_reg = S2MPS11_REG_RAMP_BUCK; + unsigned int ramp_enable = 1, enable_shift = 0; + int ret; + + switch (rdev->desc->id) { + case S2MPS11_BUCK1: + if (ramp_delay > s2mps11->ramp_delay16) + s2mps11->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay16; + + ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT; + break; + case S2MPS11_BUCK2: + enable_shift = S2MPS11_BUCK2_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + s2mps11->ramp_delay2 = ramp_delay; + ramp_shift = S2MPS11_BUCK2_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK3: + enable_shift = S2MPS11_BUCK3_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay34) + s2mps11->ramp_delay34 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay34; + + ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK4: + enable_shift = S2MPS11_BUCK4_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay34) + s2mps11->ramp_delay34 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay34; + + ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK5: + s2mps11->ramp_delay5 = ramp_delay; + ramp_shift = S2MPS11_BUCK5_RAMP_SHIFT; + break; + case S2MPS11_BUCK6: + enable_shift = S2MPS11_BUCK6_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay16) + s2mps11->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay16; + + ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT; + break; + case S2MPS11_BUCK7: + case S2MPS11_BUCK8: + case S2MPS11_BUCK10: + if (ramp_delay > s2mps11->ramp_delay7810) + s2mps11->ramp_delay7810 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay7810; + + ramp_shift = S2MPS11_BUCK7810_RAMP_SHIFT; + break; + case S2MPS11_BUCK9: + s2mps11->ramp_delay9 = ramp_delay; + ramp_shift = S2MPS11_BUCK9_RAMP_SHIFT; + break; + default: + return 0; + } + + if (!ramp_enable) + goto ramp_disable; + + if (enable_shift) { + ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; + } + } + + ramp_val = get_ramp_delay(ramp_delay); + + return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift, + ramp_val << ramp_shift); + +ramp_disable: + return regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, + 1 << enable_shift, 0); +} + static struct regulator_ops s2mps11_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -72,7 +234,8 @@ static struct regulator_ops s2mps11_buck_ops = { .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_voltage_time_sel = s2mps11_regulator_set_voltage_time_sel, + .set_ramp_delay = s2mps11_set_ramp_delay, }; #define regulator_desc_ldo1(num) { \ @@ -239,59 +402,51 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); + struct of_regulator_match rdata[S2MPS11_REGULATOR_MAX]; + struct device_node *reg_np = NULL; struct regulator_config config = { }; struct s2mps11_info *s2mps11; int i, ret; - unsigned char ramp_enable, ramp_reg = 0; - - if (!pdata) { - dev_err(pdev->dev.parent, "Platform data not supplied\n"); - return -ENODEV; - } s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info), GFP_KERNEL); if (!s2mps11) return -ENOMEM; - platform_set_drvdata(pdev, s2mps11); + if (!iodev->dev->of_node) { + if (pdata) { + goto common_reg; + } else { + dev_err(pdev->dev.parent, + "Platform data or DT node not supplied\n"); + return -ENODEV; + } + } - s2mps11->ramp_delay2 = pdata->buck2_ramp_delay; - s2mps11->ramp_delay34 = pdata->buck34_ramp_delay; - s2mps11->ramp_delay5 = pdata->buck5_ramp_delay; - s2mps11->ramp_delay16 = pdata->buck16_ramp_delay; - s2mps11->ramp_delay7810 = pdata->buck7810_ramp_delay; - s2mps11->ramp_delay9 = pdata->buck9_ramp_delay; - - s2mps11->buck6_ramp = pdata->buck6_ramp_enable; - s2mps11->buck2_ramp = pdata->buck2_ramp_enable; - s2mps11->buck3_ramp = pdata->buck3_ramp_enable; - s2mps11->buck4_ramp = pdata->buck4_ramp_enable; - - ramp_enable = (s2mps11->buck2_ramp << 3) | (s2mps11->buck3_ramp << 2) | - (s2mps11->buck4_ramp << 1) | s2mps11->buck6_ramp ; - - if (ramp_enable) { - if (s2mps11->buck2_ramp) - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay2) << 6; - if (s2mps11->buck3_ramp || s2mps11->buck4_ramp) - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay34) << 4; - sec_reg_write(iodev, S2MPS11_REG_RAMP, ramp_reg | ramp_enable); + for (i = 0; i < S2MPS11_REGULATOR_CNT; i++) + rdata[i].name = regulators[i].name; + + reg_np = of_find_node_by_name(iodev->dev->of_node, "regulators"); + if (!reg_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; } - ramp_reg &= 0x00; - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay5) << 6; - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay16) << 4; - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay7810) << 2; - ramp_reg |= get_ramp_delay(s2mps11->ramp_delay9); - sec_reg_write(iodev, S2MPS11_REG_RAMP_BUCK, ramp_reg); + of_regulator_match(&pdev->dev, reg_np, rdata, S2MPS11_REGULATOR_MAX); - for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) { +common_reg: + platform_set_drvdata(pdev, s2mps11); - config.dev = &pdev->dev; - config.regmap = iodev->regmap; - config.init_data = pdata->regulators[i].initdata; - config.driver_data = s2mps11; + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.driver_data = s2mps11; + for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) { + if (!reg_np) { + config.init_data = pdata->regulators[i].initdata; + } else { + config.init_data = rdata[i].init_data; + config.of_node = rdata[i].of_node; + } s2mps11->rdev[i] = regulator_register(®ulators[i], &config); if (IS_ERR(s2mps11->rdev[i])) { diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index 3753ed0..d8e3e12 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -717,11 +717,6 @@ static int ti_abb_probe(struct platform_device *pdev) /* Map ABB resources */ pname = "base-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); - if (!res) { - dev_err(dev, "Missing '%s' IO resource\n", pname); - ret = -ENODEV; - goto err; - } abb->base = devm_ioremap_resource(dev, res); if (IS_ERR(abb->base)) { ret = PTR_ERR(abb->base); @@ -770,11 +765,6 @@ static int ti_abb_probe(struct platform_device *pdev) pname = "ldo-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); - if (!res) { - dev_dbg(dev, "Missing '%s' IO resource\n", pname); - ret = -ENODEV; - goto skip_opt; - } abb->ldo_base = devm_ioremap_resource(dev, res); if (IS_ERR(abb->ldo_base)) { ret = PTR_ERR(abb->ldo_base); diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index 6e67be7..9392a7c 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -275,7 +275,7 @@ static int tps51632_probe(struct i2c_client *client, } } - pdata = client->dev.platform_data; + pdata = dev_get_platdata(&client->dev); if (!pdata && client->dev.of_node) pdata = of_get_tps51632_platform_data(&client->dev); if (!pdata) { diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index a490d5b..0b7ebb1 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -350,7 +350,7 @@ static int tps62360_probe(struct i2c_client *client, int i; int chip_id; - pdata = client->dev.platform_data; + pdata = dev_get_platdata(&client->dev); if (client->dev.of_node) { const struct of_device_id *match; diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 9d053e2..a15263d 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -218,7 +218,7 @@ static int tps_65023_probe(struct i2c_client *client, * init_data points to array of regulator_init structures * coming from the board-evm file. */ - init_data = client->dev.platform_data; + init_data = dev_get_platdata(&client->dev); if (!init_data) return -EIO; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 2df4616..90861d6 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -27,7 +27,7 @@ #include <linux/regulator/machine.h> #include <linux/mfd/tps65217.h> -#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t, _lr, _nlr) \ { \ .name = _name, \ .id = _id, \ @@ -40,17 +40,10 @@ .enable_reg = TPS65217_REG_ENABLE, \ .enable_mask = _em, \ .volt_table = _t, \ + .linear_ranges = _lr, \ + .n_linear_ranges = _nlr, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \ - { \ - .name = _nm, \ - .min_uV = _min, \ - .max_uV = _max, \ - .vsel_to_uv = _f1, \ - .uv_to_vsel = _f2, \ - } - static const unsigned int LDO1_VSEL_table[] = { 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, @@ -58,88 +51,26 @@ static const unsigned int LDO1_VSEL_table[] = { 2800000, 3000000, 3100000, 3300000, }; -static int tps65217_vsel_to_uv1(unsigned int vsel) -{ - int uV = 0; - - if (vsel > 63) - return -EINVAL; - - if (vsel <= 24) - uV = vsel * 25000 + 900000; - else if (vsel <= 52) - uV = (vsel - 24) * 50000 + 1500000; - else if (vsel < 56) - uV = (vsel - 52) * 100000 + 2900000; - else - uV = 3300000; - - return uV; -} - -static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel) -{ - if (uV < 0 || uV > 3300000) - return -EINVAL; - - if (uV <= 1500000) - *vsel = DIV_ROUND_UP(uV - 900000, 25000); - else if (uV <= 2900000) - *vsel = 24 + DIV_ROUND_UP(uV - 1500000, 50000); - else if (uV < 3300000) - *vsel = 52 + DIV_ROUND_UP(uV - 2900000, 100000); - else - *vsel = 56; - - return 0; -} - -static int tps65217_vsel_to_uv2(unsigned int vsel) -{ - int uV = 0; - - if (vsel > 31) - return -EINVAL; - - if (vsel <= 8) - uV = vsel * 50000 + 1500000; - else if (vsel <= 13) - uV = (vsel - 8) * 100000 + 1900000; - else - uV = (vsel - 13) * 50000 + 2400000; - - return uV; -} - -static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) -{ - if (uV < 0 || uV > 3300000) - return -EINVAL; - - if (uV <= 1900000) - *vsel = DIV_ROUND_UP(uV - 1500000, 50000); - else if (uV <= 2400000) - *vsel = 8 + DIV_ROUND_UP(uV - 1900000, 100000); - else - *vsel = 13 + DIV_ROUND_UP(uV - 2400000, 50000); - - return 0; -} +static const struct regulator_linear_range tps65217_uv1_ranges[] = { + { .min_uV = 900000, .max_uV = 1500000, .min_sel = 0, .max_sel = 24, + .uV_step = 25000 }, + { .min_uV = 1550000, .max_uV = 1800000, .min_sel = 25, .max_sel = 30, + .uV_step = 50000 }, + { .min_uV = 1850000, .max_uV = 2900000, .min_sel = 31, .max_sel = 52, + .uV_step = 50000 }, + { .min_uV = 3000000, .max_uV = 3200000, .min_sel = 53, .max_sel = 55, + .uV_step = 100000 }, + { .min_uV = 3300000, .max_uV = 3300000, .min_sel = 56, .max_sel = 62, + .uV_step = 0 }, +}; -static struct tps_info tps65217_pmic_regs[] = { - TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1), - TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1), - TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1), - TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL), - TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1), - TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2), - TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2), +static const struct regulator_linear_range tps65217_uv2_ranges[] = { + { .min_uV = 1500000, .max_uV = 1900000, .min_sel = 0, .max_sel = 8, + .uV_step = 50000 }, + { .min_uV = 2000000, .max_uV = 2400000, .min_sel = 9, .max_sel = 13, + .uV_step = 100000 }, + { .min_uV = 2450000, .max_uV = 3300000, .min_sel = 14, .max_sel = 31, + .uV_step = 50000 }, }; static int tps65217_pmic_enable(struct regulator_dev *dev) @@ -192,49 +123,6 @@ static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, return ret; } -static int tps65217_pmic_map_voltage(struct regulator_dev *dev, - int min_uV, int max_uV) -{ - - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int sel, rid = rdev_get_id(dev); - int ret; - - /* LDO1 uses regulator_map_voltage_iterate() */ - if (rid == TPS65217_LDO_1) - return -EINVAL; - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - if (min_uV < tps->info[rid]->min_uV) - min_uV = tps->info[rid]->min_uV; - - if (max_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV) - return -EINVAL; - - ret = tps->info[rid]->uv_to_vsel(min_uV, &sel); - if (ret) - return ret; - - return sel; -} - -static int tps65217_pmic_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - if (selector >= dev->desc->n_voltages) - return -EINVAL; - - return tps->info[rid]->vsel_to_uv(selector); -} - /* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ static struct regulator_ops tps65217_pmic_ops = { .is_enabled = regulator_is_enabled_regmap, @@ -242,8 +130,8 @@ static struct regulator_ops tps65217_pmic_ops = { .disable = tps65217_pmic_disable, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, - .list_voltage = tps65217_pmic_list_voltage, - .map_voltage = tps65217_pmic_map_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, }; /* Operations permitted on LDO1 */ @@ -259,27 +147,33 @@ static struct regulator_ops tps65217_pmic_ldo1_ops = { static const struct regulator_desc regulators[] = { TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC1_EN, NULL), + TPS65217_ENABLE_DC1_EN, NULL, tps65217_uv1_ranges, + 2), /* DCDC1 voltage range: 900000 ~ 1800000 */ TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC2_EN, NULL), + TPS65217_ENABLE_DC2_EN, NULL, tps65217_uv1_ranges, + ARRAY_SIZE(tps65217_uv1_ranges)), TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC3_EN, NULL), + TPS65217_ENABLE_DC3_EN, NULL, tps65217_uv1_ranges, + 1), /* DCDC3 voltage range: 900000 ~ 1500000 */ TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, - TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table), + TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table, NULL, 0), TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, - TPS65217_ENABLE_LDO2_EN, NULL), + TPS65217_ENABLE_LDO2_EN, NULL, tps65217_uv1_ranges, + ARRAY_SIZE(tps65217_uv1_ranges)), TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, - NULL), + NULL, tps65217_uv2_ranges, + ARRAY_SIZE(tps65217_uv2_ranges)), TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, - NULL), + NULL, tps65217_uv2_ranges, + ARRAY_SIZE(tps65217_uv2_ranges)), }; #ifdef CONFIG_OF @@ -368,8 +262,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) continue; /* Register the regulators */ - tps->info[i] = &tps65217_pmic_regs[i]; - config.dev = tps->dev; config.init_data = reg_data; config.driver_data = tps; diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 1094393..62e8d28 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -601,7 +601,7 @@ static int pmic_probe(struct spi_device *spi) struct regulator_config config = { }; int ret = 0, i; - init_data = dev->platform_data; + init_data = dev_get_platdata(dev); if (!init_data) { dev_err(dev, "could not find regulator platform data\n"); return -EINVAL; diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 17e994e..281e52a 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -118,6 +118,15 @@ struct tps65912_reg { int eco_reg; }; +static const struct regulator_linear_range tps65912_ldo_ranges[] = { + { .min_uV = 800000, .max_uV = 1600000, .min_sel = 0, .max_sel = 32, + .uV_step = 25000 }, + { .min_uV = 1650000, .max_uV = 3000000, .min_sel = 33, .max_sel = 60, + .uV_step = 50000 }, + { .min_uV = 3100000, .max_uV = 3300000, .min_sel = 61, .max_sel = 63, + .uV_step = 100000 }, +}; + static int tps65912_get_range(struct tps65912_reg *pmic, int id) { struct tps65912 *mfd = pmic->mfd; @@ -184,20 +193,6 @@ static unsigned long tps65912_vsel_to_uv_range3(u8 vsel) return uv; } -static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel) -{ - unsigned long uv = 0; - - if (vsel <= 32) - uv = ((vsel * 25000) + 800000); - else if (vsel > 32 && vsel <= 60) - uv = (((vsel - 32) * 50000) + 1600000); - else if (vsel > 60) - uv = (((vsel - 60) * 100000) + 3000000); - - return uv; -} - static int tps65912_get_ctrl_register(int id) { if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) @@ -376,9 +371,6 @@ static int tps65912_list_voltage(struct regulator_dev *dev, unsigned selector) struct tps65912_reg *pmic = rdev_get_drvdata(dev); int range, voltage = 0, id = rdev_get_id(dev); - if (id >= TPS65912_REG_LDO1 && id <= TPS65912_REG_LDO10) - return tps65912_vsel_to_uv_ldo(selector); - if (id > TPS65912_REG_DCDC4) return -EINVAL; @@ -456,7 +448,8 @@ static struct regulator_ops tps65912_ops_ldo = { .disable = tps65912_reg_disable, .get_voltage_sel = tps65912_get_voltage_sel, .set_voltage_sel = tps65912_set_voltage_sel, - .list_voltage = tps65912_list_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, }; static int tps65912_probe(struct platform_device *pdev) @@ -495,8 +488,14 @@ static int tps65912_probe(struct platform_device *pdev) pmic->desc[i].name = info->name; pmic->desc[i].id = i; pmic->desc[i].n_voltages = 64; - pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ? - &tps65912_ops_ldo : &tps65912_ops_dcdc); + if (i > TPS65912_REG_DCDC4) { + pmic->desc[i].ops = &tps65912_ops_ldo; + pmic->desc[i].linear_ranges = tps65912_ldo_ranges; + pmic->desc[i].n_linear_ranges = + ARRAY_SIZE(tps65912_ldo_ranges); + } else { + pmic->desc[i].ops = &tps65912_ops_dcdc; + } pmic->desc[i].type = REGULATOR_VOLTAGE; pmic->desc[i].owner = THIS_MODULE; range = tps65912_get_range(pmic, i); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 93bc4f4..78aae4c 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1108,7 +1108,7 @@ static int twlreg_probe(struct platform_device *pdev) drvdata = NULL; } else { id = pdev->id; - initdata = pdev->dev.platform_data; + initdata = dev_get_platdata(&pdev->dev); for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) { template = twl_of_match[i].data; if (template && template->desc.id == id) diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c index a7c8deb..765acc1 100644 --- a/drivers/regulator/userspace-consumer.c +++ b/drivers/regulator/userspace-consumer.c @@ -111,7 +111,7 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) struct userspace_consumer_data *drvdata; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -EINVAL; diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index a9d4284..f53e78b 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -287,7 +287,7 @@ static const struct attribute_group regulator_virtual_attr_group = { static int regulator_virtual_probe(struct platform_device *pdev) { - char *reg_id = pdev->dev.platform_data; + char *reg_id = dev_get_platdata(&pdev->dev); struct virtual_consumer_data *drvdata; int ret; diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 46938cf..11861cb 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -451,7 +451,7 @@ static void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, static int wm831x_buckv_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id; struct wm831x_dcdc *dcdc; @@ -624,7 +624,7 @@ static struct regulator_ops wm831x_buckp_ops = { static int wm831x_buckp_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id; struct wm831x_dcdc *dcdc; @@ -770,7 +770,7 @@ static struct regulator_ops wm831x_boostp_ops = { static int wm831x_boostp_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id = pdev->id % ARRAY_SIZE(pdata->dcdc); struct wm831x_dcdc *dcdc; @@ -880,7 +880,7 @@ static struct regulator_ops wm831x_epe_ops = { static int wm831x_epe_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id = pdev->id % ARRAY_SIZE(pdata->epe); struct wm831x_dcdc *dcdc; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 16ebdf9..4eb373d 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -151,7 +151,7 @@ static irqreturn_t wm831x_isink_irq(int irq, void *data) static int wm831x_isink_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct wm831x_isink *isink; int id = pdev->id % ARRAY_SIZE(pdata->isink); struct regulator_config config = { }; diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 9ff883f..1432b26 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -62,41 +62,12 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data) * General purpose LDOs */ -#define WM831X_GP_LDO_SELECTOR_LOW 0xe -#define WM831X_GP_LDO_MAX_SELECTOR 0x1f - -static int wm831x_gp_ldo_list_voltage(struct regulator_dev *rdev, - unsigned int selector) -{ - /* 0.9-1.6V in 50mV steps */ - if (selector <= WM831X_GP_LDO_SELECTOR_LOW) - return 900000 + (selector * 50000); - /* 1.7-3.3V in 100mV steps */ - if (selector <= WM831X_GP_LDO_MAX_SELECTOR) - return 1600000 + ((selector - WM831X_GP_LDO_SELECTOR_LOW) - * 100000); - return -EINVAL; -} - -static int wm831x_gp_ldo_map_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int volt, vsel; - - if (min_uV < 900000) - vsel = 0; - else if (min_uV < 1700000) - vsel = ((min_uV - 900000) / 50000); - else - vsel = ((min_uV - 1700000) / 100000) - + WM831X_GP_LDO_SELECTOR_LOW + 1; - - volt = wm831x_gp_ldo_list_voltage(rdev, vsel); - if (volt < min_uV || volt > max_uV) - return -EINVAL; - - return vsel; -} +static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = { + { .min_uV = 900000, .max_uV = 1650000, .min_sel = 0, .max_sel = 14, + .uV_step = 50000 }, + { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31, + .uV_step = 100000 }, +}; static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) @@ -105,7 +76,7 @@ static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, struct wm831x *wm831x = ldo->wm831x; int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; - sel = wm831x_gp_ldo_map_voltage(rdev, uV, uV); + sel = regulator_map_voltage_linear_range(rdev, uV, uV); if (sel < 0) return sel; @@ -230,8 +201,8 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev, static struct regulator_ops wm831x_gp_ldo_ops = { - .list_voltage = wm831x_gp_ldo_list_voltage, - .map_voltage = wm831x_gp_ldo_map_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage, @@ -250,7 +221,7 @@ static struct regulator_ops wm831x_gp_ldo_ops = { static int wm831x_gp_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; @@ -290,7 +261,7 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) ldo->desc.id = id; ldo->desc.type = REGULATOR_VOLTAGE; - ldo->desc.n_voltages = WM831X_GP_LDO_MAX_SELECTOR + 1; + ldo->desc.n_voltages = 32; ldo->desc.ops = &wm831x_gp_ldo_ops; ldo->desc.owner = THIS_MODULE; ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL; @@ -299,6 +270,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) ldo->desc.enable_mask = 1 << id; ldo->desc.bypass_reg = ldo->base; ldo->desc.bypass_mask = WM831X_LDO1_SWI; + ldo->desc.linear_ranges = wm831x_gp_ldo_ranges; + ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_gp_ldo_ranges); config.dev = pdev->dev.parent; if (pdata) @@ -358,43 +331,12 @@ static struct platform_driver wm831x_gp_ldo_driver = { * Analogue LDOs */ - -#define WM831X_ALDO_SELECTOR_LOW 0xc -#define WM831X_ALDO_MAX_SELECTOR 0x1f - -static int wm831x_aldo_list_voltage(struct regulator_dev *rdev, - unsigned int selector) -{ - /* 1-1.6V in 50mV steps */ - if (selector <= WM831X_ALDO_SELECTOR_LOW) - return 1000000 + (selector * 50000); - /* 1.7-3.5V in 100mV steps */ - if (selector <= WM831X_ALDO_MAX_SELECTOR) - return 1600000 + ((selector - WM831X_ALDO_SELECTOR_LOW) - * 100000); - return -EINVAL; -} - -static int wm831x_aldo_map_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - int volt, vsel; - - if (min_uV < 1000000) - vsel = 0; - else if (min_uV < 1700000) - vsel = ((min_uV - 1000000) / 50000); - else - vsel = ((min_uV - 1700000) / 100000) - + WM831X_ALDO_SELECTOR_LOW + 1; - - volt = wm831x_aldo_list_voltage(rdev, vsel); - if (volt < min_uV || volt > max_uV) - return -EINVAL; - - return vsel; - -} +static const struct regulator_linear_range wm831x_aldo_ranges[] = { + { .min_uV = 1000000, .max_uV = 1650000, .min_sel = 0, .max_sel = 12, + .uV_step = 50000 }, + { .min_uV = 1700000, .max_uV = 3500000, .min_sel = 13, .max_sel = 31, + .uV_step = 100000 }, +}; static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) @@ -403,7 +345,7 @@ static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, struct wm831x *wm831x = ldo->wm831x; int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; - sel = wm831x_aldo_map_voltage(rdev, uV, uV); + sel = regulator_map_voltage_linear_range(rdev, uV, uV); if (sel < 0) return sel; @@ -486,8 +428,8 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev) } static struct regulator_ops wm831x_aldo_ops = { - .list_voltage = wm831x_aldo_list_voltage, - .map_voltage = wm831x_aldo_map_voltage, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_suspend_voltage = wm831x_aldo_set_suspend_voltage, @@ -505,7 +447,7 @@ static struct regulator_ops wm831x_aldo_ops = { static int wm831x_aldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; @@ -545,7 +487,9 @@ static int wm831x_aldo_probe(struct platform_device *pdev) ldo->desc.id = id; ldo->desc.type = REGULATOR_VOLTAGE; - ldo->desc.n_voltages = WM831X_ALDO_MAX_SELECTOR + 1; + ldo->desc.n_voltages = 32; + ldo->desc.linear_ranges = wm831x_aldo_ranges; + ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_aldo_ranges); ldo->desc.ops = &wm831x_aldo_ops; ldo->desc.owner = THIS_MODULE; ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL; @@ -661,7 +605,7 @@ static struct regulator_ops wm831x_alive_ldo_ops = { static int wm831x_alive_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 7f0fa22..835b5f0 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -542,41 +542,12 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, return 0; } -static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector > WM8350_LDO1_VSEL_MASK) - return -EINVAL; - - if (selector < 16) - return (selector * 50000) + 900000; - else - return ((selector - 16) * 100000) + 1800000; -} - -static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV) -{ - int volt, sel; - int min_mV = min_uV / 1000; - int max_mV = max_uV / 1000; - - if (min_mV < 900 || min_mV > 3300) - return -EINVAL; - if (max_mV < 900 || max_mV > 3300) - return -EINVAL; - - if (min_mV < 1800) /* step size is 50mV < 1800mV */ - sel = DIV_ROUND_UP(min_uV - 900, 50); - else /* step size is 100mV > 1800mV */ - sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16; - - volt = wm8350_ldo_list_voltage(rdev, sel); - if (volt < min_uV || volt > max_uV) - return -EINVAL; - - return sel; -} +static const struct regulator_linear_range wm8350_ldo_ranges[] = { + { .min_uV = 900000, .max_uV = 1750000, .min_sel = 0, .max_sel = 15, + .uV_step = 50000 }, + { .min_uV = 1800000, .max_uV = 3300000, .min_sel = 16, .max_sel = 31, + .uV_step = 100000 }, +}; static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { @@ -603,7 +574,7 @@ static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) return -EINVAL; } - sel = wm8350_ldo_map_voltage(rdev, uV, uV); + sel = regulator_map_voltage_linear_range(rdev, uV, uV); if (sel < 0) return -EINVAL; @@ -998,10 +969,10 @@ static struct regulator_ops wm8350_dcdc2_5_ops = { }; static struct regulator_ops wm8350_ldo_ops = { - .map_voltage = wm8350_ldo_map_voltage, + .map_voltage = regulator_map_voltage_linear_range, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .list_voltage = wm8350_ldo_list_voltage, + .list_voltage = regulator_list_voltage_linear_range, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -1108,6 +1079,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO1, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO1_VSEL_MASK + 1, + .linear_ranges = wm8350_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), .vsel_reg = WM8350_LDO1_CONTROL, .vsel_mask = WM8350_LDO1_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1121,6 +1094,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO2, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO2_VSEL_MASK + 1, + .linear_ranges = wm8350_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), .vsel_reg = WM8350_LDO2_CONTROL, .vsel_mask = WM8350_LDO2_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1134,6 +1109,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO3, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO3_VSEL_MASK + 1, + .linear_ranges = wm8350_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), .vsel_reg = WM8350_LDO3_CONTROL, .vsel_mask = WM8350_LDO3_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1147,6 +1124,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO4, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO4_VSEL_MASK + 1, + .linear_ranges = wm8350_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges), .vsel_reg = WM8350_LDO4_CONTROL, .vsel_mask = WM8350_LDO4_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1222,7 +1201,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev) } config.dev = &pdev->dev; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = dev_get_drvdata(&pdev->dev); config.regmap = wm8350->regmap; diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index a09f03e..58f51be 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -19,47 +19,21 @@ #include <linux/regulator/driver.h> #include <linux/mfd/wm8400-private.h> -static int wm8400_ldo_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - if (selector > WM8400_LDO1_VSEL_MASK) - return -EINVAL; - - if (selector < 15) - return 900000 + (selector * 50000); - else - return 1700000 + ((selector - 15) * 100000); -} - -static int wm8400_ldo_map_voltage(struct regulator_dev *dev, - int min_uV, int max_uV) -{ - u16 val; - int volt; - - if (min_uV < 900000 || min_uV > 3300000) - return -EINVAL; - - if (min_uV < 1700000) /* Steps of 50mV from 900mV; */ - val = DIV_ROUND_UP(min_uV - 900000, 50000); - else /* Steps of 100mV from 1700mV */ - val = DIV_ROUND_UP(min_uV - 1700000, 100000) + 15; - - volt = wm8400_ldo_list_voltage(dev, val); - if (volt < min_uV || volt > max_uV) - return -EINVAL; - - return val; -} +static const struct regulator_linear_range wm8400_ldo_ranges[] = { + { .min_uV = 900000, .max_uV = 1600000, .min_sel = 0, .max_sel = 14, + .uV_step = 50000 }, + { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31, + .uV_step = 100000 }, +}; static struct regulator_ops wm8400_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .list_voltage = wm8400_ldo_list_voltage, + .list_voltage = regulator_list_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .map_voltage = wm8400_ldo_map_voltage, + .map_voltage = regulator_map_voltage_linear_range, }; static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) @@ -155,6 +129,8 @@ static struct regulator_desc regulators[] = { .enable_reg = WM8400_LDO1_CONTROL, .enable_mask = WM8400_LDO1_ENA, .n_voltages = WM8400_LDO1_VSEL_MASK + 1, + .linear_ranges = wm8400_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), .vsel_reg = WM8400_LDO1_CONTROL, .vsel_mask = WM8400_LDO1_VSEL_MASK, .type = REGULATOR_VOLTAGE, @@ -167,6 +143,8 @@ static struct regulator_desc regulators[] = { .enable_reg = WM8400_LDO2_CONTROL, .enable_mask = WM8400_LDO2_ENA, .n_voltages = WM8400_LDO2_VSEL_MASK + 1, + .linear_ranges = wm8400_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), .type = REGULATOR_VOLTAGE, .vsel_reg = WM8400_LDO2_CONTROL, .vsel_mask = WM8400_LDO2_VSEL_MASK, @@ -179,6 +157,8 @@ static struct regulator_desc regulators[] = { .enable_reg = WM8400_LDO3_CONTROL, .enable_mask = WM8400_LDO3_ENA, .n_voltages = WM8400_LDO3_VSEL_MASK + 1, + .linear_ranges = wm8400_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), .vsel_reg = WM8400_LDO3_CONTROL, .vsel_mask = WM8400_LDO3_VSEL_MASK, .type = REGULATOR_VOLTAGE, @@ -191,6 +171,8 @@ static struct regulator_desc regulators[] = { .enable_reg = WM8400_LDO4_CONTROL, .enable_mask = WM8400_LDO4_ENA, .n_voltages = WM8400_LDO4_VSEL_MASK + 1, + .linear_ranges = wm8400_ldo_ranges, + .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), .vsel_reg = WM8400_LDO4_CONTROL, .vsel_mask = WM8400_LDO4_VSEL_MASK, .type = REGULATOR_VOLTAGE, @@ -233,7 +215,7 @@ static int wm8400_regulator_probe(struct platform_device *pdev) struct regulator_dev *rdev; config.dev = &pdev->dev; - config.init_data = pdev->dev.platform_data; + config.init_data = dev_get_platdata(&pdev->dev); config.driver_data = wm8400; config.regmap = wm8400->regmap; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 8f2a8a7..5ee2a20 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -125,7 +125,7 @@ static const struct regulator_init_data wm8994_ldo_default[] = { static int wm8994_ldo_probe(struct platform_device *pdev) { struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); - struct wm8994_pdata *pdata = wm8994->dev->platform_data; + struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev); int id = pdev->id % ARRAY_SIZE(pdata->ldo); struct regulator_config config = { }; struct wm8994_ldo *ldo; diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 1d4c8fe..c82fe65 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -102,10 +102,13 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) zfcp_erp_action_dismiss(&port->erp_action); - else - shost_for_each_device(sdev, port->adapter->scsi_host) + else { + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) zfcp_erp_action_dismiss_lun(sdev); + spin_unlock(port->adapter->scsi_host->host_lock); + } } static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) @@ -592,9 +595,11 @@ static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, { struct scsi_device *sdev; - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) _zfcp_erp_lun_reopen(sdev, clear, id, 0); + spin_unlock(port->adapter->scsi_host->host_lock); } static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) @@ -1434,8 +1439,10 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) atomic_set_mask(common_mask, &port->status); read_unlock_irqrestore(&adapter->port_list_lock, flags); - shost_for_each_device(sdev, adapter->scsi_host) + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1469,11 +1476,13 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) } read_unlock_irqrestore(&adapter->port_list_lock, flags); - shost_for_each_device(sdev, adapter->scsi_host) { + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1487,16 +1496,19 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) { struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; + unsigned long flags; atomic_set_mask(mask, &port->status); if (!common_mask) return; - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** @@ -1511,6 +1523,7 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; + unsigned long flags; atomic_clear_mask(mask, &port->status); @@ -1520,13 +1533,15 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) if (clear_counter) atomic_set(&port->erp_counter, 0); - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 665e3cf..de0598e 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -224,11 +224,9 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) { - spin_lock_irq(&qdio->req_q_lock); if (atomic_read(&qdio->req_q_free) || !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return 1; - spin_unlock_irq(&qdio->req_q_lock); return 0; } @@ -246,9 +244,8 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) { long ret; - spin_unlock_irq(&qdio->req_q_lock); - ret = wait_event_interruptible_timeout(qdio->req_q_wq, - zfcp_qdio_sbal_check(qdio), 5 * HZ); + ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq, + zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ); if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return -EIO; @@ -262,7 +259,6 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1"); } - spin_lock_irq(&qdio->req_q_lock); return -EIO; } diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 3f01bbf..8906392 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -27,6 +27,16 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ zfcp_sysfs_##_feat##_##_name##_show, NULL); +#define ZFCP_DEFINE_ATTR_CONST(_feat, _name, _format, _value) \ +static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + return sprintf(buf, _format, _value); \ +} \ +static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ + zfcp_sysfs_##_feat##_##_name##_show, NULL); + #define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \ static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \ struct device_attribute *at,\ @@ -75,6 +85,8 @@ ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n", ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n", (zfcp_unit_sdev_status(unit) & ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_shared, "%d\n", 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_readonly, "%d\n", 0); static ssize_t zfcp_sysfs_port_failed_show(struct device *dev, struct device_attribute *attr, @@ -347,6 +359,8 @@ static struct attribute *zfcp_unit_attrs[] = { &dev_attr_unit_in_recovery.attr, &dev_attr_unit_status.attr, &dev_attr_unit_access_denied.attr, + &dev_attr_unit_access_shared.attr, + &dev_attr_unit_access_readonly.attr, NULL }; static struct attribute_group zfcp_unit_attr_group = { diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 48b2918..92ff027 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1353,7 +1353,6 @@ config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" depends on PCI && SCSI select SCSI_FC_ATTRS - select GENERIC_CSUM select CRC_T10DIF help This lpfc driver supports the Emulex LightPulse diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 5456f5c..4a21957 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -221,7 +221,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt = 0x01; for (i = 0; i < PM8001_MAX_INB_NUM; i++) { pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt = - PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30); + PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30); pm8001_ha->inbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[IB + i].phys_addr_hi; pm8001_ha->inbnd_q_tbl[i].lower_base_addr = @@ -247,7 +247,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) } for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) { pm8001_ha->outbnd_q_tbl[i].element_size_cnt = - PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30); + PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30); pm8001_ha->outbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[OB + i].phys_addr_hi; pm8001_ha->outbnd_q_tbl[i].lower_base_addr = diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 7f77210..9f91030 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -275,7 +275,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) { pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt = - PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30); + PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30); pm8001_ha->inbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[IB + i].phys_addr_hi; pm8001_ha->inbnd_q_tbl[i].lower_base_addr = @@ -301,7 +301,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) } for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++) { pm8001_ha->outbnd_q_tbl[i].element_size_cnt = - PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30); + PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30); pm8001_ha->outbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[OB + i].phys_addr_hi; pm8001_ha->outbnd_q_tbl[i].lower_base_addr = diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 89cbbab..0170d4c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -70,14 +70,14 @@ config SPI_ATH79 config SPI_ATMEL tristate "Atmel SPI Controller" - depends on (ARCH_AT91 || AVR32) + depends on (ARCH_AT91 || AVR32 || COMPILE_TEST) help This selects a driver for the Atmel SPI Controller, present on many AT32 (AVR32) and AT91 (ARM) chips. config SPI_BCM2835 tristate "BCM2835 SPI controller" - depends on ARCH_BCM2835 + depends on ARCH_BCM2835 || COMPILE_TEST help This selects a driver for the Broadcom BCM2835 SPI master. @@ -88,10 +88,17 @@ config SPI_BCM2835 config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" - depends on BLACKFIN + depends on BLACKFIN && !BF60x help This is the SPI controller master driver for Blackfin 5xx processor. +config SPI_BFIN_V3 + tristate "SPI controller v3 for Blackfin" + depends on BF60x + help + This is the SPI controller v3 master driver + found on Blackfin 60x processor. + config SPI_BFIN_SPORT tristate "SPI bus via Blackfin SPORT" depends on BLACKFIN @@ -151,15 +158,22 @@ config SPI_COLDFIRE_QSPI config SPI_DAVINCI tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller" - depends on ARCH_DAVINCI + depends on ARCH_DAVINCI || ARCH_KEYSTONE select SPI_BITBANG select TI_EDMA help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_EFM32 + tristate "EFM32 SPI controller" + depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) + select SPI_BITBANG + help + Driver for the spi controller found on Energy Micro's EFM32 SoCs. + config SPI_EP93XX tristate "Cirrus Logic EP93xx SPI controller" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST help This enables using the Cirrus EP93xx SPI controller in master mode. @@ -191,7 +205,7 @@ config SPI_GPIO config SPI_IMX tristate "Freescale i.MX SPI controllers" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST select SPI_BITBANG default m if IMX_HAVE_PLATFORM_SPI_IMX help @@ -248,6 +262,13 @@ config SPI_FSL_SPI This also enables using the Aeroflex Gaisler GRLIB SPI controller in master mode. +config SPI_FSL_DSPI + tristate "Freescale DSPI controller" + select SPI_BITBANG + help + This enables support for the Freescale DSPI controller in master + mode. VF610 platform uses the controller. + config SPI_FSL_ESPI bool "Freescale eSPI controller" depends on FSL_SOC @@ -280,20 +301,28 @@ config SPI_OMAP_UWIRE config SPI_OMAP24XX tristate "McSPI driver for OMAP" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST help SPI master controller for OMAP24XX and later Multichannel SPI (McSPI) modules. +config SPI_TI_QSPI + tristate "DRA7xxx QSPI controller support" + depends on ARCH_OMAP2PLUS || COMPILE_TEST + help + QSPI master controller for DRA7xxx used for flash devices. + This device supports single, dual and quad read support, while + it only supports single write mode. + config SPI_OMAP_100K tristate "OMAP SPI 100K" - depends on ARCH_OMAP850 || ARCH_OMAP730 + depends on ARCH_OMAP850 || ARCH_OMAP730 || COMPILE_TEST help OMAP SPI 100K master controller for omap7xx boards. config SPI_ORION tristate "Orion SPI master" - depends on PLAT_ORION + depends on PLAT_ORION || COMPILE_TEST help This enables using the SPI master controller on the Orion chips. @@ -341,7 +370,7 @@ config SPI_PXA2XX_PCI config SPI_RSPI tristate "Renesas RSPI controller" - depends on SUPERH + depends on SUPERH && SH_DMAE_BASE help SPI driver for Renesas RSPI blocks. @@ -385,7 +414,7 @@ config SPI_SH_MSIOF config SPI_SH tristate "SuperH SPI controller" - depends on SUPERH + depends on SUPERH || COMPILE_TEST help SPI driver for SuperH SPI blocks. @@ -398,13 +427,13 @@ config SPI_SH_SCI config SPI_SH_HSPI tristate "SuperH HSPI controller" - depends on ARCH_SHMOBILE + depends on ARCH_SHMOBILE || COMPILE_TEST help SPI driver for SuperH HSPI blocks. config SPI_SIRF tristate "CSR SiRFprimaII SPI controller" - depends on ARCH_SIRF + depends on SIRF_DMA select SPI_BITBANG help SPI driver for CSR SiRFprimaII SoCs @@ -418,7 +447,7 @@ config SPI_MXS config SPI_TEGRA114 tristate "NVIDIA Tegra114 SPI Controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST help SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller is different than the older SoCs SPI controller and also register interface @@ -426,7 +455,7 @@ config SPI_TEGRA114 config SPI_TEGRA20_SFLASH tristate "Nvidia Tegra20 Serial flash Controller" - depends on ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST help SPI driver for Nvidia Tegra20 Serial flash Controller interface. The main usecase of this controller is to use spi flash as boot @@ -434,7 +463,7 @@ config SPI_TEGRA20_SFLASH config SPI_TEGRA20_SLINK tristate "Nvidia Tegra20/Tegra30 SLINK Controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. @@ -457,7 +486,7 @@ config SPI_TOPCLIFF_PCH config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" - depends on GPIOLIB && CPU_TX49XX + depends on GPIOLIB && (CPU_TX49XX || COMPILE_TEST) help SPI driver for Toshiba TXx9 MIPS SoCs diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 33f9c09..ab8d864 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o +obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o @@ -27,9 +28,11 @@ obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o +obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o +obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o @@ -46,6 +49,7 @@ obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o +obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 81b9adb..f38855f 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -103,16 +103,6 @@ static void altera_spi_chipsel(struct spi_device *spi, int value) } } -static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) -{ - return 0; -} - -static int altera_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) { if (hw->tx) { @@ -134,7 +124,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) hw->tx = t->tx_buf; hw->rx = t->rx_buf; hw->count = 0; - hw->bytes_per_word = t->bits_per_word / 8; + hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); hw->len = t->len / hw->bytes_per_word; if (hw->irq >= 0) { @@ -150,12 +140,12 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); } else { - /* send the first byte */ - writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); - - while (1) { + while (hw->count < hw->len) { unsigned int rxd; + writel(hw_txbyte(hw, hw->count), + hw->base + ALTERA_SPI_TXDATA); + while (!(readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)) cpu_relax(); @@ -174,14 +164,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) } hw->count++; - - if (hw->count < hw->len) - writel(hw_txbyte(hw, hw->count), - hw->base + ALTERA_SPI_TXDATA); - else - break; } - } return hw->count * hw->bytes_per_word; @@ -217,7 +200,7 @@ static irqreturn_t altera_spi_irq(int irq, void *dev) static int altera_spi_probe(struct platform_device *pdev) { - struct altera_spi_platform_data *platp = pdev->dev.platform_data; + struct altera_spi_platform_data *platp = dev_get_platdata(&pdev->dev); struct altera_spi *hw; struct spi_master *master; struct resource *res; @@ -231,7 +214,6 @@ static int altera_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = 16; master->mode_bits = SPI_CS_HIGH; - master->setup = altera_spi_setup; hw = spi_master_get_devdata(master); platform_set_drvdata(pdev, hw); @@ -240,21 +222,16 @@ static int altera_spi_probe(struct platform_device *pdev) hw->bitbang.master = spi_master_get(master); if (!hw->bitbang.master) return err; - hw->bitbang.setup_transfer = altera_spi_setupxfer; hw->bitbang.chipselect = altera_spi_chipsel; hw->bitbang.txrx_bufs = altera_spi_txrx; /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto exit_busy; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) - goto exit_busy; - hw->base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!hw->base) - goto exit_busy; + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) { + err = PTR_ERR(hw->base); + goto exit; + } /* program defaults into the registers */ hw->imr = 0; /* disable spi interrupts */ writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); @@ -281,9 +258,6 @@ static int altera_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); return 0; - -exit_busy: - err = -EBUSY; exit: spi_master_put(master); return err; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 0e06407..37bad95 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -221,7 +221,7 @@ static int ath79_spi_probe(struct platform_device *pdev) sp = spi_master_get_devdata(master); platform_set_drvdata(pdev, sp); - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->setup = ath79_spi_setup; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index ea1ec00..fd7cc56 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -360,12 +360,12 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) gpio_set_value(asd->npcs_pin, !active); } -static void atmel_spi_lock(struct atmel_spi *as) +static void atmel_spi_lock(struct atmel_spi *as) __acquires(&as->lock) { spin_lock_irqsave(&as->lock, as->flags); } -static void atmel_spi_unlock(struct atmel_spi *as) +static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock) { spin_unlock_irqrestore(&as->lock, as->flags); } @@ -629,9 +629,9 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, goto err_dma; dev_dbg(master->dev.parent, - " start dma xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " start dma xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, + xfer->rx_buf, (unsigned long long)xfer->rx_dma); /* Enable relevant interrupts */ spi_writel(as, IER, SPI_BIT(OVRES)); @@ -732,9 +732,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, spi_writel(as, TCR, len); dev_dbg(&msg->spi->dev, - " start xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, + (unsigned long long)xfer->tx_dma, xfer->rx_buf, + (unsigned long long)xfer->rx_dma); } else { xfer = as->next_transfer; remaining = as->next_remaining_bytes; @@ -771,9 +772,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, spi_writel(as, TNCR, len); dev_dbg(&msg->spi->dev, - " next xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " next xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, + (unsigned long long)xfer->tx_dma, xfer->rx_buf, + (unsigned long long)xfer->rx_dma); ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES); } else { spi_writel(as, RNCR, 0); @@ -1579,7 +1581,9 @@ static int atmel_spi_probe(struct platform_device *pdev) goto out_unmap_regs; /* Initialize the hardware */ - clk_enable(clk); + ret = clk_prepare_enable(clk); + if (ret) + goto out_unmap_regs; spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ if (as->caps.has_wdrbt) { @@ -1609,7 +1613,7 @@ out_free_dma: spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable(clk); + clk_disable_unprepare(clk); free_irq(irq, master); out_unmap_regs: iounmap(as->regs); @@ -1661,7 +1665,7 @@ static int atmel_spi_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); - clk_disable(as->clk); + clk_disable_unprepare(as->clk); clk_put(as->clk); free_irq(as->irq, master); iounmap(as->regs); @@ -1678,7 +1682,7 @@ static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); - clk_disable(as->clk); + clk_disable_unprepare(as->clk); return 0; } @@ -1687,7 +1691,7 @@ static int atmel_spi_resume(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); - clk_enable(as->clk); + return clk_prepare_enable(as->clk); return 0; } diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index e196555..1d00d9b3 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -776,7 +776,7 @@ static int au1550_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); hw->master = spi_master_get(master); - hw->pdata = pdev->dev.platform_data; + hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; if (hw->pdata == NULL) { diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index a4185e4..52c8148 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -314,7 +314,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); master->mode_bits = BCM2835_SPI_MODE_BITS; - master->bits_per_word_mask = BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(8); master->bus_num = -1; master->num_chipselect = 3; master->transfer_one_message = bcm2835_spi_transfer_one; @@ -325,12 +325,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) init_completion(&bs->done); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "could not get memory resource\n"); - err = -ENODEV; - goto out_master_put; - } - bs->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(bs->regs)) { err = PTR_ERR(bs->regs); @@ -383,7 +377,7 @@ out_master_put: static int bcm2835_spi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct bcm2835_spi *bs = spi_master_get_devdata(master); free_irq(bs->irq, master); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 9fd7a39..536b0e3 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -231,24 +231,6 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, return 0; } -static int bcm63xx_spi_prepare_transfer(struct spi_master *master) -{ - struct bcm63xx_spi *bs = spi_master_get_devdata(master); - - pm_runtime_get_sync(&bs->pdev->dev); - - return 0; -} - -static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) -{ - struct bcm63xx_spi *bs = spi_master_get_devdata(master); - - pm_runtime_put(&bs->pdev->dev); - - return 0; -} - static int bcm63xx_spi_transfer_one(struct spi_master *master, struct spi_message *m) { @@ -353,20 +335,13 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) { struct resource *r; struct device *dev = &pdev->dev; - struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; + struct bcm63xx_spi_pdata *pdata = dev_get_platdata(&pdev->dev); int irq; struct spi_master *master; struct clk *clk; struct bcm63xx_spi *bs; int ret; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(dev, "no iomem\n"); - ret = -ENXIO; - goto out; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq\n"); @@ -393,6 +368,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); bs->pdev = pdev; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); bs->regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(bs->regs)) { ret = PTR_ERR(bs->regs); @@ -412,11 +388,10 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; - master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; - master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; master->transfer_one_message = bcm63xx_spi_transfer_one; master->mode_bits = MODEBITS; master->bits_per_word_mask = SPI_BPW_MASK(8); + master->auto_runtime_pm = true; bs->msg_type_shift = pdata->msg_type_shift; bs->msg_ctl_width = pdata->msg_ctl_width; bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); @@ -480,8 +455,7 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bcm63xx_spi_suspend(struct device *dev) { - struct spi_master *master = - platform_get_drvdata(to_platform_device(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct bcm63xx_spi *bs = spi_master_get_devdata(master); spi_master_suspend(master); @@ -493,8 +467,7 @@ static int bcm63xx_spi_suspend(struct device *dev) static int bcm63xx_spi_resume(struct device *dev) { - struct spi_master *master = - platform_get_drvdata(to_platform_device(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct bcm63xx_spi *bs = spi_master_get_devdata(master); clk_prepare_enable(bs->clk); diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 07ec597..91921b5 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -756,7 +756,7 @@ static int bfin_sport_spi_probe(struct platform_device *pdev) struct bfin_sport_spi_master_data *drv_data; int status; - platform_info = dev->platform_data; + platform_info = dev_get_platdata(dev); /* Allocate master with space for drv_data */ master = spi_alloc_master(dev, sizeof(*master) + 16); diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c new file mode 100644 index 0000000..f4bf813 --- /dev/null +++ b/drivers/spi/spi-bfin-v3.c @@ -0,0 +1,965 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2013 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +#include <asm/bfin_spi3.h> +#include <asm/cacheflush.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +enum bfin_spi_state { + START_STATE, + RUNNING_STATE, + DONE_STATE, + ERROR_STATE +}; + +struct bfin_spi_master; + +struct bfin_spi_transfer_ops { + void (*write) (struct bfin_spi_master *); + void (*read) (struct bfin_spi_master *); + void (*duplex) (struct bfin_spi_master *); +}; + +/* runtime info for spi master */ +struct bfin_spi_master { + /* SPI framework hookup */ + struct spi_master *master; + + /* Regs base of SPI controller */ + struct bfin_spi_regs __iomem *regs; + + /* Pin request list */ + u16 *pin_req; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct bfin_spi_device *cur_chip; + unsigned transfer_len; + + /* transfer buffer */ + void *tx; + void *tx_end; + void *rx; + void *rx_end; + + /* dma info */ + unsigned int tx_dma; + unsigned int rx_dma; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; + unsigned long dummy_buffer; /* used in unidirectional transfer */ + unsigned long tx_dma_size; + unsigned long rx_dma_size; + int tx_num; + int rx_num; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + unsigned long sclk; + enum bfin_spi_state state; + + const struct bfin_spi_transfer_ops *ops; +}; + +struct bfin_spi_device { + u32 control; + u32 clock; + u32 ssel; + + u8 cs; + u16 cs_chg_udelay; /* Some devices require > 255usec delay */ + u32 cs_gpio; + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; + const struct bfin_spi_transfer_ops *ops; +}; + +static void bfin_spi_enable(struct bfin_spi_master *drv_data) +{ + bfin_write_or(&drv_data->regs->control, SPI_CTL_EN); +} + +static void bfin_spi_disable(struct bfin_spi_master *drv_data) +{ + bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN); +} + +/* Caculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = sclk / speed_hz; + + if (spi_clock) + spi_clock--; + return spi_clock; +} + +static int bfin_spi_flush(struct bfin_spi_master *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + /* wait for stop and clear stat */ + while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) + cpu_relax(); + + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + + return limit; +} + +/* Chip select operation functions for cs_change flag */ +static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) + bfin_write_and(&drv_data->regs->ssel, ~chip->ssel); + else + gpio_set_value(chip->cs_gpio, 0); +} + +static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) + bfin_write_or(&drv_data->regs->ssel, chip->ssel); + else + gpio_set_value(chip->cs_gpio, 1); + + /* Move delay here for consistency */ + if (chip->cs_chg_udelay) + udelay(chip->cs_chg_udelay); +} + +/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ +static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) + bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8); +} + +static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) + bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8)); +} + +/* stop controller and re-config current chip*/ +static void bfin_spi_restore_state(struct bfin_spi_master *drv_data) +{ + struct bfin_spi_device *chip = drv_data->cur_chip; + + /* Clear status and disable clock */ + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + bfin_write(&drv_data->regs->rx_control, 0x0); + bfin_write(&drv_data->regs->tx_control, 0x0); + bfin_spi_disable(drv_data); + + SSYNC(); + + /* Load the registers */ + bfin_write(&drv_data->regs->control, chip->control); + bfin_write(&drv_data->regs->clock, chip->clock); + + bfin_spi_enable(drv_data); + drv_data->tx_num = drv_data->rx_num = 0; + /* we always choose tx transfer initiate */ + bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN); + bfin_write(&drv_data->regs->tx_control, + SPI_TXCTL_TEN | SPI_TXCTL_TTI); + bfin_spi_cs_active(drv_data, chip); +} + +/* discard invalid rx data and empty rfifo */ +static inline void dummy_read(struct bfin_spi_master *drv_data) +{ + while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)) + bfin_read(&drv_data->regs->rfifo); +} + +static void bfin_spi_u8_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u8_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { + .write = bfin_spi_u8_write, + .read = bfin_spi_u8_read, + .duplex = bfin_spi_u8_duplex, +}; + +static void bfin_spi_u16_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); + drv_data->tx += 2; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u16_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); + drv_data->tx += 2; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { + .write = bfin_spi_u16_write, + .read = bfin_spi_u16_read, + .duplex = bfin_spi_u16_duplex, +}; + +static void bfin_spi_u32_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); + drv_data->tx += 4; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u32_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); + drv_data->tx += 4; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = { + .write = bfin_spi_u32_write, + .read = bfin_spi_u32_read, + .duplex = bfin_spi_u32_duplex, +}; + + +/* test if there is more transfer to be done */ +static void bfin_spi_next_transfer(struct bfin_spi_master *drv) +{ + struct spi_message *msg = drv->cur_msg; + struct spi_transfer *t = drv->cur_transfer; + + /* Move to next transfer */ + if (t->transfer_list.next != &msg->transfers) { + drv->cur_transfer = list_entry(t->transfer_list.next, + struct spi_transfer, transfer_list); + drv->state = RUNNING_STATE; + } else { + drv->state = DONE_STATE; + drv->cur_transfer = NULL; + } +} + +static void bfin_spi_giveback(struct bfin_spi_master *drv_data) +{ + struct bfin_spi_device *chip = drv_data->cur_chip; + + bfin_spi_cs_deactive(drv_data, chip); + spi_finalize_current_message(drv_data->master); +} + +static int bfin_spi_setup_transfer(struct bfin_spi_master *drv) +{ + struct spi_transfer *t = drv->cur_transfer; + u32 cr, cr_width; + + if (t->tx_buf) { + drv->tx = (void *)t->tx_buf; + drv->tx_end = drv->tx + t->len; + } else { + drv->tx = NULL; + } + + if (t->rx_buf) { + drv->rx = t->rx_buf; + drv->rx_end = drv->rx + t->len; + } else { + drv->rx = NULL; + } + + drv->transfer_len = t->len; + + /* bits per word setup */ + switch (t->bits_per_word) { + case 8: + cr_width = SPI_CTL_SIZE08; + drv->ops = &bfin_bfin_spi_transfer_ops_u8; + break; + case 16: + cr_width = SPI_CTL_SIZE16; + drv->ops = &bfin_bfin_spi_transfer_ops_u16; + break; + case 32: + cr_width = SPI_CTL_SIZE32; + drv->ops = &bfin_bfin_spi_transfer_ops_u32; + break; + default: + return -EINVAL; + } + cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE; + cr |= cr_width; + bfin_write(&drv->regs->control, cr); + + /* speed setup */ + bfin_write(&drv->regs->clock, + hz_to_spi_clock(drv->sclk, t->speed_hz)); + return 0; +} + +static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_transfer *t = drv_data->cur_transfer; + struct spi_message *msg = drv_data->cur_msg; + struct bfin_spi_device *chip = drv_data->cur_chip; + u32 dma_config; + unsigned long word_count, word_size; + void *tx_buf, *rx_buf; + + switch (t->bits_per_word) { + case 8: + dma_config = WDSIZE_8 | PSIZE_8; + word_count = drv_data->transfer_len; + word_size = 1; + break; + case 16: + dma_config = WDSIZE_16 | PSIZE_16; + word_count = drv_data->transfer_len / 2; + word_size = 2; + break; + default: + dma_config = WDSIZE_32 | PSIZE_32; + word_count = drv_data->transfer_len / 4; + word_size = 4; + break; + } + + if (!drv_data->rx) { + tx_buf = drv_data->tx; + rx_buf = &drv_data->dummy_buffer; + drv_data->tx_dma_size = drv_data->transfer_len; + drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, 0); + } else if (!drv_data->tx) { + drv_data->dummy_buffer = chip->tx_dummy_val; + tx_buf = &drv_data->dummy_buffer; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); + drv_data->rx_dma_size = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, 0); + set_dma_x_modify(drv_data->rx_dma, word_size); + } else { + tx_buf = drv_data->tx; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = drv_data->rx_dma_size + = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, word_size); + } + + drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)tx_buf, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->tx_dma_addr)) + return -ENOMEM; + + drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)rx_buf, + drv_data->rx_dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->rx_dma_addr)) { + dma_unmap_single(&msg->spi->dev, + drv_data->tx_dma_addr, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + return -ENOMEM; + } + + dummy_read(drv_data); + set_dma_x_count(drv_data->tx_dma, word_count); + set_dma_x_count(drv_data->rx_dma, word_count); + set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); + set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); + dma_config |= DMAFLOW_STOP | RESTART | DI_EN; + set_dma_config(drv_data->tx_dma, dma_config); + set_dma_config(drv_data->rx_dma, dma_config | WNR); + enable_dma(drv_data->tx_dma); + enable_dma(drv_data->rx_dma); + SSYNC(); + + bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); + SSYNC(); + bfin_write(&drv_data->regs->tx_control, + SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); + + return 0; +} + +static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + + if (!drv_data->rx) { + /* write only half duplex */ + drv_data->ops->write(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } else if (!drv_data->tx) { + /* read only half duplex */ + drv_data->ops->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + return -EIO; + } else { + /* full duplex mode */ + drv_data->ops->duplex(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } + + if (!bfin_spi_flush(drv_data)) + return -EIO; + msg->actual_length += drv_data->transfer_len; + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +static void bfin_spi_pump_transfers(unsigned long data) +{ + struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; + struct spi_message *msg = NULL; + struct spi_transfer *t = NULL; + struct bfin_spi_device *chip = NULL; + int ret; + + /* Get current state information */ + msg = drv_data->cur_msg; + t = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + msg->status = -EIO; + bfin_spi_giveback(drv_data); + return; + } + + if (drv_data->state == RUNNING_STATE) { + if (t->delay_usecs) + udelay(t->delay_usecs); + if (t->cs_change) + bfin_spi_cs_deactive(drv_data, chip); + bfin_spi_next_transfer(drv_data); + t = drv_data->cur_transfer; + } + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + msg->status = 0; + bfin_spi_giveback(drv_data); + return; + } + + if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return; + } + + ret = bfin_spi_setup_transfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } + + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + bfin_spi_cs_active(drv_data, chip); + drv_data->state = RUNNING_STATE; + + if (chip->enable_dma) + ret = bfin_spi_dma_xfer(drv_data); + else + ret = bfin_spi_pio_xfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } +} + +static int bfin_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + drv_data->cur_msg = m; + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + bfin_spi_restore_state(drv_data); + + drv_data->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, transfer_list); + + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +#define MAX_SPI_SSEL 7 + +static const u16 ssel[][MAX_SPI_SSEL] = { + {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, + P_SPI0_SSEL4, P_SPI0_SSEL5, + P_SPI0_SSEL6, P_SPI0_SSEL7}, + + {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, + P_SPI1_SSEL4, P_SPI1_SSEL5, + P_SPI1_SSEL6, P_SPI1_SSEL7}, + + {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, + P_SPI2_SSEL4, P_SPI2_SSEL5, + P_SPI2_SSEL6, P_SPI2_SSEL7}, +}; + +static int bfin_spi_setup(struct spi_device *spi) +{ + struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); + struct bfin_spi_device *chip = spi_get_ctldata(spi); + u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; + int ret = -EINVAL; + + if (!chip) { + struct bfin_spi3_chip *chip_info = spi->controller_data; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, "can not allocate chip data\n"); + return -ENOMEM; + } + if (chip_info) { + if (chip_info->control & ~bfin_ctl_reg) { + dev_err(&spi->dev, + "do not set bits that the SPI framework manages\n"); + goto error; + } + chip->control = chip_info->control; + chip->cs_chg_udelay = chip_info->cs_chg_udelay; + chip->tx_dummy_val = chip_info->tx_dummy_val; + chip->enable_dma = chip_info->enable_dma; + } + chip->cs = spi->chip_select; + if (chip->cs < MAX_CTRL_CS) { + chip->ssel = (1 << chip->cs) << 8; + ret = peripheral_request(ssel[spi->master->bus_num] + [chip->cs-1], dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "peripheral_request() error\n"); + goto error; + } + } else { + chip->cs_gpio = chip->cs - MAX_CTRL_CS; + ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "gpio_request_one() error\n"); + goto error; + } + } + spi_set_ctldata(spi, chip); + } + + /* force a default base state */ + chip->control &= bfin_ctl_reg; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + /* we choose software to controll cs */ + chip->control &= ~SPI_CTL_ASSEL; + + chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); + + bfin_spi_cs_enable(drv_data, chip); + bfin_spi_cs_deactive(drv_data, chip); + + return 0; +error: + if (chip) { + kfree(chip); + spi_set_ctldata(spi, NULL); + } + + return ret; +} + +static void bfin_spi_cleanup(struct spi_device *spi) +{ + struct bfin_spi_device *chip = spi_get_ctldata(spi); + struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); + + if (!chip) + return; + + if (chip->cs < MAX_CTRL_CS) { + peripheral_free(ssel[spi->master->bus_num] + [chip->cs-1]); + bfin_spi_cs_disable(drv_data, chip); + } else { + gpio_free(chip->cs_gpio); + } + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id) +{ + struct bfin_spi_master *drv_data = dev_id; + u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); + + clear_dma_irqstat(drv_data->tx_dma); + if (dma_stat & DMA_DONE) { + drv_data->tx_num++; + } else { + dev_err(&drv_data->master->dev, + "spi tx dma error: %d\n", dma_stat); + if (drv_data->tx) + drv_data->state = ERROR_STATE; + } + bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF); + return IRQ_HANDLED; +} + +static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) +{ + struct bfin_spi_master *drv_data = dev_id; + struct spi_message *msg = drv_data->cur_msg; + u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); + + clear_dma_irqstat(drv_data->rx_dma); + if (dma_stat & DMA_DONE) { + drv_data->rx_num++; + /* we may fail on tx dma */ + if (drv_data->state != ERROR_STATE) + msg->actual_length += drv_data->transfer_len; + } else { + drv_data->state = ERROR_STATE; + dev_err(&drv_data->master->dev, + "spi rx dma error: %d\n", dma_stat); + } + bfin_write(&drv_data->regs->tx_control, 0); + bfin_write(&drv_data->regs->rx_control, 0); + if (drv_data->rx_num != drv_data->tx_num) + dev_dbg(&drv_data->master->dev, + "dma interrupt missing: tx=%d,rx=%d\n", + drv_data->tx_num, drv_data->rx_num); + tasklet_schedule(&drv_data->pump_transfers); + return IRQ_HANDLED; +} + +static int bfin_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bfin_spi3_master *info = dev_get_platdata(dev); + struct spi_master *master; + struct bfin_spi_master *drv_data; + struct resource *mem, *res; + unsigned int tx_dma, rx_dma; + unsigned long sclk; + int ret; + + if (!info) { + dev_err(dev, "platform data missing!\n"); + return -ENODEV; + } + + sclk = get_sclk1(); + if (!sclk) { + dev_err(dev, "can not get sclk1\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "can not get tx dma resource\n"); + return -ENXIO; + } + tx_dma = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "can not get rx dma resource\n"); + return -ENXIO; + } + rx_dma = res->start; + + /* allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(*drv_data)); + if (!master) { + dev_err(dev, "can not alloc spi_master\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + /* the mode bits supported by this driver */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + + master->bus_num = pdev->id; + master->num_chipselect = info->num_chipselect; + master->cleanup = bfin_spi_cleanup; + master->setup = bfin_spi_setup; + master->transfer_one_message = bfin_spi_transfer_one_message; + master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->tx_dma = tx_dma; + drv_data->rx_dma = rx_dma; + drv_data->pin_req = info->pin_req; + drv_data->sclk = sclk; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + ret = PTR_ERR(drv_data->regs); + goto err_put_master; + } + + /* request tx and rx dma */ + ret = request_dma(tx_dma, "SPI_TX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI TX DMA channel\n"); + goto err_put_master; + } + set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data); + + ret = request_dma(rx_dma, "SPI_RX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI RX DMA channel\n"); + goto err_free_tx_dma; + } + set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data); + + /* request CLK, MOSI and MISO */ + ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3"); + if (ret < 0) { + dev_err(dev, "can not request spi pins\n"); + goto err_free_rx_dma; + } + + bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); + bfin_write(&drv_data->regs->ssel, 0x0000FE00); + bfin_write(&drv_data->regs->delay, 0x0); + + tasklet_init(&drv_data->pump_transfers, + bfin_spi_pump_transfers, (unsigned long)drv_data); + /* register with the SPI framework */ + ret = spi_register_master(master); + if (ret) { + dev_err(dev, "can not register spi master\n"); + goto err_free_peripheral; + } + + return ret; + +err_free_peripheral: + peripheral_free_list(drv_data->pin_req); +err_free_rx_dma: + free_dma(rx_dma); +err_free_tx_dma: + free_dma(tx_dma); +err_put_master: + spi_master_put(master); + + return ret; +} + +static int bfin_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + bfin_spi_disable(drv_data); + + peripheral_free_list(drv_data->pin_req); + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + + spi_unregister_master(drv_data->master); + return 0; +} + +#ifdef CONFIG_PM +static int bfin_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + spi_master_suspend(master); + + drv_data->control = bfin_read(&drv_data->regs->control); + drv_data->ssel = bfin_read(&drv_data->regs->ssel); + + bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); + bfin_write(&drv_data->regs->ssel, 0x0000FE00); + dma_disable_irq(drv_data->rx_dma); + dma_disable_irq(drv_data->tx_dma); + + return 0; +} + +static int bfin_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + int ret = 0; + + /* bootrom may modify spi and dma status when resume in spi boot mode */ + disable_dma(drv_data->rx_dma); + + dma_enable_irq(drv_data->rx_dma); + dma_enable_irq(drv_data->tx_dma); + bfin_write(&drv_data->regs->control, drv_data->control); + bfin_write(&drv_data->regs->ssel, drv_data->ssel); + + ret = spi_master_resume(master); + if (ret) { + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + } + + return ret; +} +#endif +static const struct dev_pm_ops bfin_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume) +}; + +MODULE_ALIAS("platform:bfin-spi3"); +static struct platform_driver bfin_spi_driver = { + .driver = { + .name = "bfin-spi3", + .owner = THIS_MODULE, + .pm = &bfin_spi_pm_ops, + }, + .remove = bfin_spi_remove, +}; + +module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 59a7342..45bdf73 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1271,7 +1271,7 @@ static int bfin_spi_probe(struct platform_device *pdev) struct resource *res; int status = 0; - platform_info = dev->platform_data; + platform_info = dev_get_platdata(dev); /* Allocate master with space for drv_data */ master = spi_alloc_master(dev, sizeof(*drv_data)); diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index a63d7da..e3946e4 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -255,150 +255,140 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) * Drivers can provide word-at-a-time i/o primitives, or provide * transfer-at-a-time ones to leverage dma or fifo hardware. */ -static void bitbang_work(struct work_struct *work) + +static int spi_bitbang_prepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang = - container_of(work, struct spi_bitbang, work); + struct spi_bitbang *bitbang; unsigned long flags; - struct spi_message *m, *_m; + + bitbang = spi_master_get_devdata(spi); spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; - list_for_each_entry_safe(m, _m, &bitbang->queue, queue) { - struct spi_device *spi; - unsigned nsecs; - struct spi_transfer *t = NULL; - unsigned tmp; - unsigned cs_change; - int status; - int do_setup = -1; - - list_del(&m->queue); - spin_unlock_irqrestore(&bitbang->lock, flags); - - /* FIXME this is made-up ... the correct value is known to - * word-at-a-time bitbang code, and presumably chipselect() - * should enforce these requirements too? - */ - nsecs = 100; + spin_unlock_irqrestore(&bitbang->lock, flags); - spi = m->spi; - tmp = 0; - cs_change = 1; - status = 0; + return 0; +} - list_for_each_entry (t, &m->transfers, transfer_list) { - - /* override speed or wordsize? */ - if (t->speed_hz || t->bits_per_word) - do_setup = 1; - - /* init (-1) or override (1) transfer params */ - if (do_setup != 0) { - status = bitbang->setup_transfer(spi, t); - if (status < 0) - break; - if (do_setup == -1) - do_setup = 0; - } - - /* set up default clock polarity, and activate chip; - * this implicitly updates clock and spi modes as - * previously recorded for this device via setup(). - * (and also deselects any other chip that might be - * selected ...) - */ - if (cs_change) { - bitbang->chipselect(spi, BITBANG_CS_ACTIVE); - ndelay(nsecs); - } - cs_change = t->cs_change; - if (!t->tx_buf && !t->rx_buf && t->len) { - status = -EINVAL; - break; - } +static int spi_bitbang_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct spi_bitbang *bitbang; + unsigned nsecs; + struct spi_transfer *t = NULL; + unsigned cs_change; + int status; + int do_setup = -1; + struct spi_device *spi = m->spi; + + bitbang = spi_master_get_devdata(master); + + /* FIXME this is made-up ... the correct value is known to + * word-at-a-time bitbang code, and presumably chipselect() + * should enforce these requirements too? + */ + nsecs = 100; - /* transfer data. the lower level code handles any - * new dma mappings it needs. our caller always gave - * us dma-safe buffers. - */ - if (t->len) { - /* REVISIT dma API still needs a designated - * DMA_ADDR_INVALID; ~0 might be better. - */ - if (!m->is_dma_mapped) - t->rx_dma = t->tx_dma = 0; - status = bitbang->txrx_bufs(spi, t); - } - if (status > 0) - m->actual_length += status; - if (status != t->len) { - /* always report some kind of error */ - if (status >= 0) - status = -EREMOTEIO; + cs_change = 1; + status = 0; + + list_for_each_entry (t, &m->transfers, transfer_list) { + + /* override speed or wordsize? */ + if (t->speed_hz || t->bits_per_word) + do_setup = 1; + + /* init (-1) or override (1) transfer params */ + if (do_setup != 0) { + status = bitbang->setup_transfer(spi, t); + if (status < 0) break; - } - status = 0; - - /* protocol tweaks before next transfer */ - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { - /* sometimes a short mid-message deselect of the chip - * may be needed to terminate a mode or command - */ - ndelay(nsecs); - bitbang->chipselect(spi, BITBANG_CS_INACTIVE); - ndelay(nsecs); - } + if (do_setup == -1) + do_setup = 0; } - m->status = status; - m->complete(m->context); + /* set up default clock polarity, and activate chip; + * this implicitly updates clock and spi modes as + * previously recorded for this device via setup(). + * (and also deselects any other chip that might be + * selected ...) + */ + if (cs_change) { + bitbang->chipselect(spi, BITBANG_CS_ACTIVE); + ndelay(nsecs); + } + cs_change = t->cs_change; + if (!t->tx_buf && !t->rx_buf && t->len) { + status = -EINVAL; + break; + } - /* normally deactivate chipselect ... unless no error and - * cs_change has hinted that the next message will probably - * be for this chip too. + /* transfer data. the lower level code handles any + * new dma mappings it needs. our caller always gave + * us dma-safe buffers. */ - if (!(status == 0 && cs_change)) { + if (t->len) { + /* REVISIT dma API still needs a designated + * DMA_ADDR_INVALID; ~0 might be better. + */ + if (!m->is_dma_mapped) + t->rx_dma = t->tx_dma = 0; + status = bitbang->txrx_bufs(spi, t); + } + if (status > 0) + m->actual_length += status; + if (status != t->len) { + /* always report some kind of error */ + if (status >= 0) + status = -EREMOTEIO; + break; + } + status = 0; + + /* protocol tweaks before next transfer */ + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { + /* sometimes a short mid-message deselect of the chip + * may be needed to terminate a mode or command + */ ndelay(nsecs); bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); } + } + + m->status = status; - spin_lock_irqsave(&bitbang->lock, flags); + /* normally deactivate chipselect ... unless no error and + * cs_change has hinted that the next message will probably + * be for this chip too. + */ + if (!(status == 0 && cs_change)) { + ndelay(nsecs); + bitbang->chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(nsecs); } - bitbang->busy = 0; - spin_unlock_irqrestore(&bitbang->lock, flags); + + spi_finalize_current_message(master); + + return status; } -/** - * spi_bitbang_transfer - default submit to transfer queue - */ -int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) +static int spi_bitbang_unprepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned long flags; - int status = 0; - m->actual_length = 0; - m->status = -EINPROGRESS; - - bitbang = spi_master_get_devdata(spi->master); + bitbang = spi_master_get_devdata(spi); spin_lock_irqsave(&bitbang->lock, flags); - if (!spi->max_speed_hz) - status = -ENETDOWN; - else { - list_add_tail(&m->queue, &bitbang->queue); - queue_work(bitbang->workqueue, &bitbang->work); - } + bitbang->busy = 0; spin_unlock_irqrestore(&bitbang->lock, flags); - return status; + return 0; } -EXPORT_SYMBOL_GPL(spi_bitbang_transfer); /*----------------------------------------------------------------------*/ @@ -428,20 +418,22 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer); int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; - int status; if (!master || !bitbang->chipselect) return -EINVAL; - INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); - INIT_LIST_HEAD(&bitbang->queue); if (!master->mode_bits) master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; - if (!master->transfer) - master->transfer = spi_bitbang_transfer; + if (master->transfer || master->transfer_one_message) + return -EINVAL; + + master->prepare_transfer_hardware = spi_bitbang_prepare_hardware; + master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware; + master->transfer_one_message = spi_bitbang_transfer_one; + if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; @@ -452,34 +444,12 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) master->setup = spi_bitbang_setup; master->cleanup = spi_bitbang_cleanup; } - } else if (!master->setup) - return -EINVAL; - if (master->transfer == spi_bitbang_transfer && - !bitbang->setup_transfer) - return -EINVAL; - - /* this task is the only thing to touch the SPI bits */ - bitbang->busy = 0; - bitbang->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (bitbang->workqueue == NULL) { - status = -EBUSY; - goto err1; } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - status = spi_register_master(master); - if (status < 0) - goto err2; - - return status; - -err2: - destroy_workqueue(bitbang->workqueue); -err1: - return status; + return spi_register_master(master); } EXPORT_SYMBOL_GPL(spi_bitbang_start); @@ -490,10 +460,6 @@ int spi_bitbang_stop(struct spi_bitbang *bitbang) { spi_unregister_master(bitbang->master); - WARN_ON(!list_empty(&bitbang->queue)); - - destroy_workqueue(bitbang->workqueue); - return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_stop); diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 17965fe..5655acf 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -239,11 +239,8 @@ static int spi_clps711x_probe(struct platform_device *pdev) } dev_err(&pdev->dev, "Failed to register master\n"); - devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); clk_out: - devm_clk_put(&pdev->dev, hw->spi_clk); - err_out: while (--i >= 0) if (gpio_is_valid(hw->chipselect[i])) @@ -261,13 +258,10 @@ static int spi_clps711x_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct spi_clps711x_data *hw = spi_master_get_devdata(master); - devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); - for (i = 0; i < master->num_chipselect; i++) if (gpio_is_valid(hw->chipselect[i])) gpio_free(hw->chipselect[i]); - devm_clk_put(&pdev->dev, hw->spi_clk); spi_unregister_master(master); kfree(master); diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 0631b9d..cc5b75d 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -354,24 +354,6 @@ static int mcfqspi_transfer_one_message(struct spi_master *master, } -static int mcfqspi_prepare_transfer_hw(struct spi_master *master) -{ - struct mcfqspi *mcfqspi = spi_master_get_devdata(master); - - pm_runtime_get_sync(mcfqspi->dev); - - return 0; -} - -static int mcfqspi_unprepare_transfer_hw(struct spi_master *master) -{ - struct mcfqspi *mcfqspi = spi_master_get_devdata(master); - - pm_runtime_put_sync(mcfqspi->dev); - - return 0; -} - static int mcfqspi_setup(struct spi_device *spi) { if (spi->chip_select >= spi->master->num_chipselect) { @@ -400,7 +382,7 @@ static int mcfqspi_probe(struct platform_device *pdev) struct mcfqspi_platform_data *pdata; int status; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_dbg(&pdev->dev, "platform data is missing\n"); return -ENOENT; @@ -473,8 +455,7 @@ static int mcfqspi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->setup = mcfqspi_setup; master->transfer_one_message = mcfqspi_transfer_one_message; - master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw; - master->unprepare_transfer_hardware = mcfqspi_unprepare_transfer_hw; + master->auto_runtime_pm = true; platform_set_drvdata(pdev, master); @@ -558,7 +539,7 @@ static int mcfqspi_resume(struct device *dev) #ifdef CONFIG_PM_RUNTIME static int mcfqspi_runtime_suspend(struct device *dev) { - struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev)); + struct mcfqspi *mcfqspi = dev_get_drvdata(dev); clk_disable(mcfqspi->clk); @@ -567,7 +548,7 @@ static int mcfqspi_runtime_suspend(struct device *dev) static int mcfqspi_runtime_resume(struct device *dev) { - struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev)); + struct mcfqspi *mcfqspi = dev_get_drvdata(dev); clk_enable(mcfqspi->clk); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 707966b..8fbfe24 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -872,8 +872,8 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - if (pdev->dev.platform_data) { - pdata = pdev->dev.platform_data; + if (dev_get_platdata(&pdev->dev)) { + pdata = dev_get_platdata(&pdev->dev); dspi->pdata = *pdata; } else { /* update dspi pdata with that from the DT */ diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c new file mode 100644 index 0000000..7d84418 --- /dev/null +++ b/drivers/spi/spi-efm32.c @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix + * + * 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/io.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/efm32-spi.h> + +#define DRIVER_NAME "efm32-spi" + +#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask) + +#define REG_CTRL 0x00 +#define REG_CTRL_SYNC 0x0001 +#define REG_CTRL_CLKPOL 0x0100 +#define REG_CTRL_CLKPHA 0x0200 +#define REG_CTRL_MSBF 0x0400 +#define REG_CTRL_TXBIL 0x1000 + +#define REG_FRAME 0x04 +#define REG_FRAME_DATABITS__MASK 0x000f +#define REG_FRAME_DATABITS(n) ((n) - 3) + +#define REG_CMD 0x0c +#define REG_CMD_RXEN 0x0001 +#define REG_CMD_RXDIS 0x0002 +#define REG_CMD_TXEN 0x0004 +#define REG_CMD_TXDIS 0x0008 +#define REG_CMD_MASTEREN 0x0010 + +#define REG_STATUS 0x10 +#define REG_STATUS_TXENS 0x0002 +#define REG_STATUS_TXC 0x0020 +#define REG_STATUS_TXBL 0x0040 +#define REG_STATUS_RXDATAV 0x0080 + +#define REG_CLKDIV 0x14 + +#define REG_RXDATAX 0x18 +#define REG_RXDATAX_RXDATA__MASK 0x01ff +#define REG_RXDATAX_PERR 0x4000 +#define REG_RXDATAX_FERR 0x8000 + +#define REG_TXDATA 0x34 + +#define REG_IF 0x40 +#define REG_IF_TXBL 0x0002 +#define REG_IF_RXDATAV 0x0004 + +#define REG_IFS 0x44 +#define REG_IFC 0x48 +#define REG_IEN 0x4c + +#define REG_ROUTE 0x54 +#define REG_ROUTE_RXPEN 0x0001 +#define REG_ROUTE_TXPEN 0x0002 +#define REG_ROUTE_CLKPEN 0x0008 +#define REG_ROUTE_LOCATION__MASK 0x0700 +#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n)) + +struct efm32_spi_ddata { + struct spi_bitbang bitbang; + + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned int rxirq, txirq; + struct efm32_spi_pdata pdata; + + /* irq data */ + struct completion done; + const u8 *tx_buf; + u8 *rx_buf; + unsigned tx_len, rx_len; + + /* chip selects */ + unsigned csgpio[]; +}; + +#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev)) +#define efm32_spi_vdbg(ddata, format, arg...) \ + dev_vdbg(ddata_to_dev(ddata), format, ##arg) + +static void efm32_spi_write32(struct efm32_spi_ddata *ddata, + u32 value, unsigned offset) +{ + writel_relaxed(value, ddata->base + offset); +} + +static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset) +{ + return readl_relaxed(ddata->base + offset); +} + +static void efm32_spi_chipselect(struct spi_device *spi, int is_on) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE); + + gpio_set_value(ddata->csgpio[spi->chip_select], value); +} + +static int efm32_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + + unsigned bpw = t->bits_per_word ?: spi->bits_per_word; + unsigned speed = t->speed_hz ?: spi->max_speed_hz; + unsigned long clkfreq = clk_get_rate(ddata->clk); + u32 clkdiv; + + efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF | + (spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) | + (spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL); + + efm32_spi_write32(ddata, + REG_FRAME_DATABITS(bpw), REG_FRAME); + + if (2 * speed >= clkfreq) + clkdiv = 0; + else + clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4); + + if (clkdiv > (1U << 21)) + return -EINVAL; + + efm32_spi_write32(ddata, clkdiv, REG_CLKDIV); + efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD); + efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD); + + return 0; +} + +static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata) +{ + u8 val = 0; + + if (ddata->tx_buf) { + val = *ddata->tx_buf; + ddata->tx_buf++; + } + + ddata->tx_len--; + efm32_spi_write32(ddata, val, REG_TXDATA); + efm32_spi_vdbg(ddata, "%s: tx 0x%x\n", __func__, val); +} + +static void efm32_spi_rx_u8(struct efm32_spi_ddata *ddata) +{ + u32 rxdata = efm32_spi_read32(ddata, REG_RXDATAX); + efm32_spi_vdbg(ddata, "%s: rx 0x%x\n", __func__, rxdata); + + if (ddata->rx_buf) { + *ddata->rx_buf = rxdata; + ddata->rx_buf++; + } + + ddata->rx_len--; +} + +static void efm32_spi_filltx(struct efm32_spi_ddata *ddata) +{ + while (ddata->tx_len && + ddata->tx_len + 2 > ddata->rx_len && + efm32_spi_read32(ddata, REG_STATUS) & REG_STATUS_TXBL) { + efm32_spi_tx_u8(ddata); + } +} + +static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int ret = -EBUSY; + + spin_lock_irq(&ddata->lock); + + if (ddata->tx_buf || ddata->rx_buf) + goto out_unlock; + + ddata->tx_buf = t->tx_buf; + ddata->rx_buf = t->rx_buf; + ddata->tx_len = ddata->rx_len = + t->len * DIV_ROUND_UP(t->bits_per_word, 8); + + efm32_spi_filltx(ddata); + + init_completion(&ddata->done); + + efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN); + + spin_unlock_irq(&ddata->lock); + + wait_for_completion(&ddata->done); + + spin_lock_irq(&ddata->lock); + + ret = t->len - max(ddata->tx_len, ddata->rx_len); + + efm32_spi_write32(ddata, 0, REG_IEN); + ddata->tx_buf = ddata->rx_buf = NULL; + +out_unlock: + spin_unlock_irq(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_rxirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&ddata->lock); + + while (ddata->rx_len > 0 && + efm32_spi_read32(ddata, REG_STATUS) & + REG_STATUS_RXDATAV) { + efm32_spi_rx_u8(ddata); + + ret = IRQ_HANDLED; + } + + if (!ddata->rx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_RXDATAV; + + efm32_spi_write32(ddata, ien, REG_IEN); + + complete(&ddata->done); + } + + spin_unlock(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_txirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + + efm32_spi_vdbg(ddata, + "%s: txlen = %u, rxlen = %u, if=0x%08x, stat=0x%08x\n", + __func__, ddata->tx_len, ddata->rx_len, + efm32_spi_read32(ddata, REG_IF), + efm32_spi_read32(ddata, REG_STATUS)); + + spin_lock(&ddata->lock); + + efm32_spi_filltx(ddata); + + efm32_spi_vdbg(ddata, "%s: txlen = %u, rxlen = %u\n", + __func__, ddata->tx_len, ddata->rx_len); + + if (!ddata->tx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_TXBL; + + efm32_spi_write32(ddata, ien, REG_IEN); + efm32_spi_vdbg(ddata, "disable TXBL\n"); + } + + spin_unlock(&ddata->lock); + + return IRQ_HANDLED; +} + +static const struct efm32_spi_pdata efm32_spi_pdata_default = { + .location = 1, +}; + +static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata) +{ + u32 reg = efm32_spi_read32(ddata, REG_ROUTE); + + return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK); +} + +static int efm32_spi_probe_dt(struct platform_device *pdev, + struct spi_master *master, struct efm32_spi_ddata *ddata) +{ + struct device_node *np = pdev->dev.of_node; + u32 location; + int ret; + + if (!np) + return 1; + + ret = of_property_read_u32(np, "location", &location); + if (!ret) { + dev_dbg(&pdev->dev, "using location %u\n", location); + } else { + /* default to location configured in hardware */ + location = efm32_spi_get_configured_location(ddata); + + dev_info(&pdev->dev, "fall back to location %u\n", location); + } + + ddata->pdata.location = location; + + /* spi core takes care about the bus number using an alias */ + master->bus_num = -1; + + return 0; +} + +static int efm32_spi_probe(struct platform_device *pdev) +{ + struct efm32_spi_ddata *ddata; + struct resource *res; + int ret; + struct spi_master *master; + struct device_node *np = pdev->dev.of_node; + unsigned int num_cs, i; + + num_cs = of_gpio_named_count(np, "cs-gpios"); + + master = spi_alloc_master(&pdev->dev, + sizeof(*ddata) + num_cs * sizeof(unsigned)); + if (!master) { + dev_dbg(&pdev->dev, + "failed to allocate spi master controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + master->dev.of_node = pdev->dev.of_node; + + master->num_chipselect = num_cs; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + + ddata = spi_master_get_devdata(master); + + ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.chipselect = efm32_spi_chipselect; + ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; + ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; + + spin_lock_init(&ddata->lock); + + ddata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ddata->clk)) { + ret = PTR_ERR(ddata->clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", ret); + goto err; + } + + for (i = 0; i < num_cs; ++i) { + ret = of_get_named_gpio(np, "cs-gpios", i); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n", + i, ret); + goto err; + } + ddata->csgpio[i] = ret; + dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]); + ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i], + GPIOF_OUT_INIT_LOW, DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to configure csgpio#%u (%d)\n", + i, ret); + goto err; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to determine base address\n"); + goto err; + } + + if (resource_size(res) < 60) { + ret = -EINVAL; + dev_err(&pdev->dev, "memory resource too small\n"); + goto err; + } + + ddata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ddata->base)) { + ret = PTR_ERR(ddata->base); + goto err; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret); + goto err; + } + + ddata->rxirq = ret; + + ret = platform_get_irq(pdev, 1); + if (ret <= 0) + ret = ddata->rxirq + 1; + + ddata->txirq = ret; + + ret = clk_prepare_enable(ddata->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret); + goto err; + } + + ret = efm32_spi_probe_dt(pdev, master, ddata); + if (ret > 0) { + /* not created by device tree */ + const struct efm32_spi_pdata *pdata = + dev_get_platdata(&pdev->dev); + + if (pdata) + ddata->pdata = *pdata; + else + ddata->pdata.location = + efm32_spi_get_configured_location(ddata); + + master->bus_num = pdev->id; + + } else if (ret < 0) { + goto err_disable_clk; + } + + efm32_spi_write32(ddata, 0, REG_IEN); + efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN | + REG_ROUTE_CLKPEN | + REG_ROUTE_LOCATION(ddata->pdata.location), REG_ROUTE); + + ret = request_irq(ddata->rxirq, efm32_spi_rxirq, + 0, DRIVER_NAME " rx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register rxirq (%d)\n", ret); + goto err_disable_clk; + } + + ret = request_irq(ddata->txirq, efm32_spi_txirq, + 0, DRIVER_NAME " tx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register txirq (%d)\n", ret); + goto err_free_rx_irq; + } + + ret = spi_bitbang_start(&ddata->bitbang); + if (ret) { + dev_err(&pdev->dev, "spi_bitbang_start failed (%d)\n", ret); + + free_irq(ddata->txirq, ddata); +err_free_rx_irq: + free_irq(ddata->rxirq, ddata); +err_disable_clk: + clk_disable_unprepare(ddata->clk); +err: + spi_master_put(master); + kfree(master); + } + + return ret; +} + +static int efm32_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct efm32_spi_ddata *ddata = spi_master_get_devdata(master); + + efm32_spi_write32(ddata, 0, REG_IEN); + + free_irq(ddata->txirq, ddata); + free_irq(ddata->rxirq, ddata); + clk_disable_unprepare(ddata->clk); + spi_master_put(master); + kfree(master); + + return 0; +} + +static const struct of_device_id efm32_spi_dt_ids[] = { + { + .compatible = "efm32,spi", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, efm32_spi_dt_ids); + +static struct platform_driver efm32_spi_driver = { + .probe = efm32_spi_probe, + .remove = efm32_spi_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = efm32_spi_dt_ids, + }, +}; +module_platform_driver(efm32_spi_driver); + +MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); +MODULE_DESCRIPTION("EFM32 SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index cad30b8..d22c00a 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -26,7 +26,6 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/workqueue.h> #include <linux/sched.h> #include <linux/scatterlist.h> #include <linux/spi/spi.h> @@ -70,19 +69,13 @@ /** * struct ep93xx_spi - EP93xx SPI controller structure - * @lock: spinlock that protects concurrent accesses to fields @running, - * @current_msg and @msg_queue * @pdev: pointer to platform device * @clk: clock for the controller * @regs_base: pointer to ioremap()'d registers * @sspdr_phys: physical address of the SSPDR register * @min_rate: minimum clock rate (in Hz) supported by the controller * @max_rate: maximum clock rate (in Hz) supported by the controller - * @running: is the queue running - * @wq: workqueue used by the driver - * @msg_work: work that is queued for the driver * @wait: wait here until given transfer is completed - * @msg_queue: queue for the messages * @current_msg: message that is currently processed (or %NULL if none) * @tx: current byte in transfer to transmit * @rx: current byte in transfer to receive @@ -96,30 +89,15 @@ * @tx_sgt: sg table for TX transfers * @zeropage: dummy page used as RX buffer when only TX buffer is passed in by * the client - * - * This structure holds EP93xx SPI controller specific information. When - * @running is %true, driver accepts transfer requests from protocol drivers. - * @current_msg is used to hold pointer to the message that is currently - * processed. If @current_msg is %NULL, it means that no processing is going - * on. - * - * Most of the fields are only written once and they can be accessed without - * taking the @lock. Fields that are accessed concurrently are: @current_msg, - * @running, and @msg_queue. */ struct ep93xx_spi { - spinlock_t lock; const struct platform_device *pdev; struct clk *clk; void __iomem *regs_base; unsigned long sspdr_phys; unsigned long min_rate; unsigned long max_rate; - bool running; - struct workqueue_struct *wq; - struct work_struct msg_work; struct completion wait; - struct list_head msg_queue; struct spi_message *current_msg; size_t tx; size_t rx; @@ -136,50 +114,36 @@ struct ep93xx_spi { /** * struct ep93xx_spi_chip - SPI device hardware settings * @spi: back pointer to the SPI device - * @rate: max rate in hz this chip supports - * @div_cpsr: cpsr (pre-scaler) divider - * @div_scr: scr divider - * @dss: bits per word (4 - 16 bits) * @ops: private chip operations - * - * This structure is used to store hardware register specific settings for each - * SPI device. Settings are written to hardware by function - * ep93xx_spi_chip_setup(). */ struct ep93xx_spi_chip { const struct spi_device *spi; - unsigned long rate; - u8 div_cpsr; - u8 div_scr; - u8 dss; struct ep93xx_spi_chip_ops *ops; }; /* converts bits per word to CR0.DSS value */ #define bits_per_word_to_dss(bpw) ((bpw) - 1) -static inline void -ep93xx_spi_write_u8(const struct ep93xx_spi *espi, u16 reg, u8 value) +static void ep93xx_spi_write_u8(const struct ep93xx_spi *espi, + u16 reg, u8 value) { - __raw_writeb(value, espi->regs_base + reg); + writeb(value, espi->regs_base + reg); } -static inline u8 -ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg) +static u8 ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg) { - return __raw_readb(spi->regs_base + reg); + return readb(spi->regs_base + reg); } -static inline void -ep93xx_spi_write_u16(const struct ep93xx_spi *espi, u16 reg, u16 value) +static void ep93xx_spi_write_u16(const struct ep93xx_spi *espi, + u16 reg, u16 value) { - __raw_writew(value, espi->regs_base + reg); + writew(value, espi->regs_base + reg); } -static inline u16 -ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg) +static u16 ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg) { - return __raw_readw(spi->regs_base + reg); + return readw(spi->regs_base + reg); } static int ep93xx_spi_enable(const struct ep93xx_spi *espi) @@ -230,17 +194,13 @@ static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi) /** * ep93xx_spi_calc_divisors() - calculates SPI clock divisors * @espi: ep93xx SPI controller struct - * @chip: divisors are calculated for this chip * @rate: desired SPI output clock rate - * - * Function calculates cpsr (clock pre-scaler) and scr divisors based on - * given @rate and places them to @chip->div_cpsr and @chip->div_scr. If, - * for some reason, divisors cannot be calculated nothing is stored and - * %-EINVAL is returned. + * @div_cpsr: pointer to return the cpsr (pre-scaler) divider + * @div_scr: pointer to return the scr divider */ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, - struct ep93xx_spi_chip *chip, - unsigned long rate) + unsigned long rate, + u8 *div_cpsr, u8 *div_scr) { unsigned long spi_clk_rate = clk_get_rate(espi->clk); int cpsr, scr; @@ -248,7 +208,7 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, /* * Make sure that max value is between values supported by the * controller. Note that minimum value is already checked in - * ep93xx_spi_transfer(). + * ep93xx_spi_transfer_one_message(). */ rate = clamp(rate, espi->min_rate, espi->max_rate); @@ -263,8 +223,8 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, for (cpsr = 2; cpsr <= 254; cpsr += 2) { for (scr = 0; scr <= 255; scr++) { if ((spi_clk_rate / (cpsr * (scr + 1))) <= rate) { - chip->div_scr = (u8)scr; - chip->div_cpsr = (u8)cpsr; + *div_scr = (u8)scr; + *div_cpsr = (u8)cpsr; return 0; } } @@ -319,73 +279,11 @@ static int ep93xx_spi_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); } - if (spi->max_speed_hz != chip->rate) { - int err; - - err = ep93xx_spi_calc_divisors(espi, chip, spi->max_speed_hz); - if (err != 0) { - spi_set_ctldata(spi, NULL); - kfree(chip); - return err; - } - chip->rate = spi->max_speed_hz; - } - - chip->dss = bits_per_word_to_dss(spi->bits_per_word); - ep93xx_spi_cs_control(spi, false); return 0; } /** - * ep93xx_spi_transfer() - queue message to be transferred - * @spi: target SPI device - * @msg: message to be transferred - * - * This function is called by SPI device drivers when they are going to transfer - * a new message. It simply puts the message in the queue and schedules - * workqueue to perform the actual transfer later on. - * - * Returns %0 on success and negative error in case of failure. - */ -static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) -{ - struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); - struct spi_transfer *t; - unsigned long flags; - - if (!msg || !msg->complete) - return -EINVAL; - - /* first validate each transfer */ - list_for_each_entry(t, &msg->transfers, transfer_list) { - if (t->speed_hz && t->speed_hz < espi->min_rate) - return -EINVAL; - } - - /* - * Now that we own the message, let's initialize it so that it is - * suitable for us. We use @msg->status to signal whether there was - * error in transfer and @msg->state is used to hold pointer to the - * current transfer (or %NULL if no active current transfer). - */ - msg->state = NULL; - msg->status = 0; - msg->actual_length = 0; - - spin_lock_irqsave(&espi->lock, flags); - if (!espi->running) { - spin_unlock_irqrestore(&espi->lock, flags); - return -ESHUTDOWN; - } - list_add_tail(&msg->queue, &espi->msg_queue); - queue_work(espi->wq, &espi->msg_work); - spin_unlock_irqrestore(&espi->lock, flags); - - return 0; -} - -/** * ep93xx_spi_cleanup() - cleans up master controller specific state * @spi: SPI device to cleanup * @@ -409,39 +307,40 @@ static void ep93xx_spi_cleanup(struct spi_device *spi) * ep93xx_spi_chip_setup() - configures hardware according to given @chip * @espi: ep93xx SPI controller struct * @chip: chip specific settings - * - * This function sets up the actual hardware registers with settings given in - * @chip. Note that no validation is done so make sure that callers validate - * settings before calling this. + * @speed_hz: transfer speed + * @bits_per_word: transfer bits_per_word */ -static void ep93xx_spi_chip_setup(const struct ep93xx_spi *espi, - const struct ep93xx_spi_chip *chip) +static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi, + const struct ep93xx_spi_chip *chip, + u32 speed_hz, u8 bits_per_word) { + u8 dss = bits_per_word_to_dss(bits_per_word); + u8 div_cpsr = 0; + u8 div_scr = 0; u16 cr0; + int err; - cr0 = chip->div_scr << SSPCR0_SCR_SHIFT; + err = ep93xx_spi_calc_divisors(espi, speed_hz, &div_cpsr, &div_scr); + if (err) + return err; + + cr0 = div_scr << SSPCR0_SCR_SHIFT; cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT; - cr0 |= chip->dss; + cr0 |= dss; dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n", - chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss); + chip->spi->mode, div_cpsr, div_scr, dss); dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0); - ep93xx_spi_write_u8(espi, SSPCPSR, chip->div_cpsr); + ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr); ep93xx_spi_write_u16(espi, SSPCR0, cr0); -} - -static inline int bits_per_word(const struct ep93xx_spi *espi) -{ - struct spi_message *msg = espi->current_msg; - struct spi_transfer *t = msg->state; - return t->bits_per_word; + return 0; } static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) { - if (bits_per_word(espi) > 8) { + if (t->bits_per_word > 8) { u16 tx_val = 0; if (t->tx_buf) @@ -460,7 +359,7 @@ static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t) { - if (bits_per_word(espi) > 8) { + if (t->bits_per_word > 8) { u16 rx_val; rx_val = ep93xx_spi_read_u16(espi, SSPDR); @@ -546,7 +445,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir) size_t len = t->len; int i, ret, nents; - if (bits_per_word(espi) > 8) + if (t->bits_per_word > 8) buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; else buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; @@ -610,7 +509,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir) } if (WARN_ON(len)) { - dev_warn(&espi->pdev->dev, "len = %d expected 0!", len); + dev_warn(&espi->pdev->dev, "len = %zu expected 0!", len); return ERR_PTR(-EINVAL); } @@ -708,37 +607,16 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, struct spi_transfer *t) { struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi); + int err; msg->state = t; - /* - * Handle any transfer specific settings if needed. We use - * temporary chip settings here and restore original later when - * the transfer is finished. - */ - if (t->speed_hz || t->bits_per_word) { - struct ep93xx_spi_chip tmp_chip = *chip; - - if (t->speed_hz) { - int err; - - err = ep93xx_spi_calc_divisors(espi, &tmp_chip, - t->speed_hz); - if (err) { - dev_err(&espi->pdev->dev, - "failed to adjust speed\n"); - msg->status = err; - return; - } - } - - if (t->bits_per_word) - tmp_chip.dss = bits_per_word_to_dss(t->bits_per_word); - - /* - * Set up temporary new hw settings for this transfer. - */ - ep93xx_spi_chip_setup(espi, &tmp_chip); + err = ep93xx_spi_chip_setup(espi, chip, t->speed_hz, t->bits_per_word); + if (err) { + dev_err(&espi->pdev->dev, + "failed to setup chip for transfer\n"); + msg->status = err; + return; } espi->rx = 0; @@ -783,9 +661,6 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, ep93xx_spi_cs_control(msg->spi, true); } } - - if (t->speed_hz || t->bits_per_word) - ep93xx_spi_chip_setup(espi, chip); } /* @@ -838,10 +713,8 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi, espi->fifo_level = 0; /* - * Update SPI controller registers according to spi device and assert - * the chipselect. + * Assert the chipselect. */ - ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi)); ep93xx_spi_cs_control(msg->spi, true); list_for_each_entry(t, &msg->transfers, transfer_list) { @@ -858,50 +731,29 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi, ep93xx_spi_disable(espi); } -#define work_to_espi(work) (container_of((work), struct ep93xx_spi, msg_work)) - -/** - * ep93xx_spi_work() - EP93xx SPI workqueue worker function - * @work: work struct - * - * Workqueue worker function. This function is called when there are new - * SPI messages to be processed. Message is taken out from the queue and then - * passed to ep93xx_spi_process_message(). - * - * After message is transferred, protocol driver is notified by calling - * @msg->complete(). In case of error, @msg->status is set to negative error - * number, otherwise it contains zero (and @msg->actual_length is updated). - */ -static void ep93xx_spi_work(struct work_struct *work) +static int ep93xx_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) { - struct ep93xx_spi *espi = work_to_espi(work); - struct spi_message *msg; + struct ep93xx_spi *espi = spi_master_get_devdata(master); + struct spi_transfer *t; - spin_lock_irq(&espi->lock); - if (!espi->running || espi->current_msg || - list_empty(&espi->msg_queue)) { - spin_unlock_irq(&espi->lock); - return; + /* first validate each transfer */ + list_for_each_entry(t, &msg->transfers, transfer_list) { + if (t->speed_hz < espi->min_rate) + return -EINVAL; } - msg = list_first_entry(&espi->msg_queue, struct spi_message, queue); - list_del_init(&msg->queue); - espi->current_msg = msg; - spin_unlock_irq(&espi->lock); - ep93xx_spi_process_message(espi, msg); + msg->state = NULL; + msg->status = 0; + msg->actual_length = 0; - /* - * Update the current message and re-schedule ourselves if there are - * more messages in the queue. - */ - spin_lock_irq(&espi->lock); + espi->current_msg = msg; + ep93xx_spi_process_message(espi, msg); espi->current_msg = NULL; - if (espi->running && !list_empty(&espi->msg_queue)) - queue_work(espi->wq, &espi->msg_work); - spin_unlock_irq(&espi->lock); - /* notify the protocol driver that we are done with this message */ - msg->complete(msg->context); + spi_finalize_current_message(master); + + return 0; } static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id) @@ -1022,16 +874,26 @@ static int ep93xx_spi_probe(struct platform_device *pdev) int irq; int error; - info = pdev->dev.platform_data; + info = dev_get_platdata(&pdev->dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq resources\n"); + return -EBUSY; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "unable to get iomem resource\n"); + return -ENODEV; + } master = spi_alloc_master(&pdev->dev, sizeof(*espi)); - if (!master) { - dev_err(&pdev->dev, "failed to allocate spi master\n"); + if (!master) return -ENOMEM; - } master->setup = ep93xx_spi_setup; - master->transfer = ep93xx_spi_transfer; + master->transfer_one_message = ep93xx_spi_transfer_one_message; master->cleanup = ep93xx_spi_cleanup; master->bus_num = pdev->id; master->num_chipselect = info->num_chipselect; @@ -1042,14 +904,13 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi = spi_master_get_devdata(master); - espi->clk = clk_get(&pdev->dev, NULL); + espi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(espi->clk)) { dev_err(&pdev->dev, "unable to get spi clock\n"); error = PTR_ERR(espi->clk); goto fail_release_master; } - spin_lock_init(&espi->lock); init_completion(&espi->wait); /* @@ -1060,55 +921,31 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi->min_rate = clk_get_rate(espi->clk) / (254 * 256); espi->pdev = pdev; - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - error = -EBUSY; - dev_err(&pdev->dev, "failed to get irq resources\n"); - goto fail_put_clock; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "unable to get iomem resource\n"); - error = -ENODEV; - goto fail_put_clock; - } - espi->sspdr_phys = res->start + SSPDR; espi->regs_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(espi->regs_base)) { error = PTR_ERR(espi->regs_base); - goto fail_put_clock; + goto fail_release_master; } error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt, 0, "ep93xx-spi", espi); if (error) { dev_err(&pdev->dev, "failed to request irq\n"); - goto fail_put_clock; + goto fail_release_master; } if (info->use_dma && ep93xx_spi_setup_dma(espi)) dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n"); - espi->wq = create_singlethread_workqueue("ep93xx_spid"); - if (!espi->wq) { - dev_err(&pdev->dev, "unable to create workqueue\n"); - error = -ENOMEM; - goto fail_free_dma; - } - INIT_WORK(&espi->msg_work, ep93xx_spi_work); - INIT_LIST_HEAD(&espi->msg_queue); - espi->running = true; - /* make sure that the hardware is disabled */ ep93xx_spi_write_u8(espi, SSPCR1, 0); error = spi_register_master(master); if (error) { dev_err(&pdev->dev, "failed to register SPI master\n"); - goto fail_free_queue; + goto fail_free_dma; } dev_info(&pdev->dev, "EP93xx SPI Controller at 0x%08lx irq %d\n", @@ -1116,12 +953,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev) return 0; -fail_free_queue: - destroy_workqueue(espi->wq); fail_free_dma: ep93xx_spi_release_dma(espi); -fail_put_clock: - clk_put(espi->clk); fail_release_master: spi_master_put(master); @@ -1133,31 +966,7 @@ static int ep93xx_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct ep93xx_spi *espi = spi_master_get_devdata(master); - spin_lock_irq(&espi->lock); - espi->running = false; - spin_unlock_irq(&espi->lock); - - destroy_workqueue(espi->wq); - - /* - * Complete remaining messages with %-ESHUTDOWN status. - */ - spin_lock_irq(&espi->lock); - while (!list_empty(&espi->msg_queue)) { - struct spi_message *msg; - - msg = list_first_entry(&espi->msg_queue, - struct spi_message, queue); - list_del_init(&msg->queue); - msg->status = -ESHUTDOWN; - spin_unlock_irq(&espi->lock); - msg->complete(msg->context); - spin_lock_irq(&espi->lock); - } - spin_unlock_irq(&espi->lock); - ep93xx_spi_release_dma(espi); - clk_put(espi->clk); spi_unregister_master(master); return 0; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c new file mode 100644 index 0000000..6cd07d1 --- /dev/null +++ b/drivers/spi/spi-fsl-dspi.c @@ -0,0 +1,557 @@ +/* + * drivers/spi/spi-fsl-dspi.c + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Freescale DSPI driver + * This file contains a driver for the Freescale DSPI + * + * 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/interrupt.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#define DRIVER_NAME "fsl-dspi" + +#define TRAN_STATE_RX_VOID 0x01 +#define TRAN_STATE_TX_VOID 0x02 +#define TRAN_STATE_WORD_ODD_NUM 0x04 + +#define DSPI_FIFO_SIZE 4 + +#define SPI_MCR 0x00 +#define SPI_MCR_MASTER (1 << 31) +#define SPI_MCR_PCSIS (0x3F << 16) +#define SPI_MCR_CLR_TXF (1 << 11) +#define SPI_MCR_CLR_RXF (1 << 10) + +#define SPI_TCR 0x08 + +#define SPI_CTAR(x) (0x0c + (x * 4)) +#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) +#define SPI_CTAR_CPOL(x) ((x) << 26) +#define SPI_CTAR_CPHA(x) ((x) << 25) +#define SPI_CTAR_LSBFE(x) ((x) << 24) +#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22) +#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20) +#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18) +#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16) +#define SPI_CTAR_CSSCK(x) (((x) & 0x0000000f) << 12) +#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8) +#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4) +#define SPI_CTAR_BR(x) ((x) & 0x0000000f) + +#define SPI_CTAR0_SLAVE 0x0c + +#define SPI_SR 0x2c +#define SPI_SR_EOQF 0x10000000 + +#define SPI_RSER 0x30 +#define SPI_RSER_EOQFE 0x10000000 + +#define SPI_PUSHR 0x34 +#define SPI_PUSHR_CONT (1 << 31) +#define SPI_PUSHR_CTAS(x) (((x) & 0x00000007) << 28) +#define SPI_PUSHR_EOQ (1 << 27) +#define SPI_PUSHR_CTCNT (1 << 26) +#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16) +#define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) + +#define SPI_PUSHR_SLAVE 0x34 + +#define SPI_POPR 0x38 +#define SPI_POPR_RXDATA(x) ((x) & 0x0000ffff) + +#define SPI_TXFR0 0x3c +#define SPI_TXFR1 0x40 +#define SPI_TXFR2 0x44 +#define SPI_TXFR3 0x48 +#define SPI_RXFR0 0x7c +#define SPI_RXFR1 0x80 +#define SPI_RXFR2 0x84 +#define SPI_RXFR3 0x88 + +#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) +#define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) +#define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) +#define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) + +#define SPI_CS_INIT 0x01 +#define SPI_CS_ASSERT 0x02 +#define SPI_CS_DROP 0x04 + +struct chip_data { + u32 mcr_val; + u32 ctar_val; + u16 void_write_data; +}; + +struct fsl_dspi { + struct spi_bitbang bitbang; + struct platform_device *pdev; + + void *base; + int irq; + struct clk *clk; + + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + char dataflags; + u8 cs; + u16 void_write_data; + + wait_queue_head_t waitq; + u32 waitflags; +}; + +static inline int is_double_byte_mode(struct fsl_dspi *dspi) +{ + return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK) + == SPI_FRAME_BITS(8)) ? 0 : 1; +} + +static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits) +{ + u32 temp; + + temp = readl(dspi->base + SPI_CTAR(dspi->cs)); + temp &= ~SPI_FRAME_BITS_MASK; + temp |= SPI_FRAME_BITS(bits); + writel(temp, dspi->base + SPI_CTAR(dspi->cs)); +} + +static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, + unsigned long clkrate) +{ + /* Valid baud rate pre-scaler values */ + int pbr_tbl[4] = {2, 3, 5, 7}; + int brs[16] = { 2, 4, 6, 8, + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768 }; + int temp, i = 0, j = 0; + + temp = clkrate / 2 / speed_hz; + + for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++) + for (j = 0; j < ARRAY_SIZE(brs); j++) { + if (pbr_tbl[i] * brs[j] >= temp) { + *pbr = i; + *br = j; + return; + } + } + + pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\ + ,we use the max prescaler value.\n", speed_hz, clkrate); + *pbr = ARRAY_SIZE(pbr_tbl) - 1; + *br = ARRAY_SIZE(brs) - 1; +} + +static int dspi_transfer_write(struct fsl_dspi *dspi) +{ + int tx_count = 0; + int tx_word; + u16 d16; + u8 d8; + u32 dspi_pushr = 0; + int first = 1; + + tx_word = is_double_byte_mode(dspi); + + /* If we are in word mode, but only have a single byte to transfer + * then switch to byte mode temporarily. Will switch back at the + * end of the transfer. + */ + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + set_bit_mode(dspi, 8); + tx_word = 0; + } + + while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { + if (tx_word) { + if (dspi->len == 1) + break; + + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + d16 = *(u16 *)dspi->tx; + dspi->tx += 2; + } else { + d16 = dspi->void_write_data; + } + + dspi_pushr = SPI_PUSHR_TXDATA(d16) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + + dspi->len -= 2; + } else { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + + d8 = *(u8 *)dspi->tx; + dspi->tx++; + } else { + d8 = (u8)dspi->void_write_data; + } + + dspi_pushr = SPI_PUSHR_TXDATA(d8) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + + dspi->len--; + } + + if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { + /* last transfer in the transfer */ + dspi_pushr |= SPI_PUSHR_EOQ; + } else if (tx_word && (dspi->len == 1)) + dspi_pushr |= SPI_PUSHR_EOQ; + + if (first) { + first = 0; + dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ + } + + writel(dspi_pushr, dspi->base + SPI_PUSHR); + tx_count++; + } + + return tx_count * (tx_word + 1); +} + +static int dspi_transfer_read(struct fsl_dspi *dspi) +{ + int rx_count = 0; + int rx_word = is_double_byte_mode(dspi); + u16 d; + while ((dspi->rx < dspi->rx_end) + && (rx_count < DSPI_FIFO_SIZE)) { + if (rx_word) { + if ((dspi->rx_end - dspi->rx) == 1) + break; + + d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u16 *)dspi->rx = d; + dspi->rx += 2; + + } else { + d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u8 *)dspi->rx = d; + dspi->rx++; + } + rx_count++; + } + + return rx_count; +} + +static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + dspi->cur_transfer = t; + dspi->cur_chip = spi_get_ctldata(spi); + dspi->cs = spi->chip_select; + dspi->void_write_data = dspi->cur_chip->void_write_data; + + dspi->dataflags = 0; + dspi->tx = (void *)t->tx_buf; + dspi->tx_end = dspi->tx + t->len; + dspi->rx = t->rx_buf; + dspi->rx_end = dspi->rx + t->len; + dspi->len = t->len; + + if (!dspi->rx) + dspi->dataflags |= TRAN_STATE_RX_VOID; + + if (!dspi->tx) + dspi->dataflags |= TRAN_STATE_TX_VOID; + + writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR); + writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs)); + writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER); + + if (t->speed_hz) + writel(dspi->cur_chip->ctar_val, + dspi->base + SPI_CTAR(dspi->cs)); + + dspi_transfer_write(dspi); + + if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) + dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); + dspi->waitflags = 0; + + return t->len - dspi->len; +} + +static void dspi_chipselect(struct spi_device *spi, int value) +{ + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + u32 pushr = readl(dspi->base + SPI_PUSHR); + + switch (value) { + case BITBANG_CS_ACTIVE: + pushr |= SPI_PUSHR_CONT; + case BITBANG_CS_INACTIVE: + pushr &= ~SPI_PUSHR_CONT; + } + + writel(pushr, dspi->base + SPI_PUSHR); +} + +static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct chip_data *chip; + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + unsigned char br = 0, pbr = 0, fmsz = 0; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (chip == NULL) { + chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + } + + chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; + if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { + fmsz = spi->bits_per_word - 1; + } else { + pr_err("Invalid wordsize\n"); + kfree(chip); + return -ENODEV; + } + + chip->void_write_data = 0; + + hz_to_spi_baud(&pbr, &br, + spi->max_speed_hz, clk_get_rate(dspi->clk)); + + chip->ctar_val = SPI_CTAR_FMSZ(fmsz) + | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) + | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0) + | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0) + | SPI_CTAR_PBR(pbr) + | SPI_CTAR_BR(br); + + spi_set_ctldata(spi, chip); + + return 0; +} + +static int dspi_setup(struct spi_device *spi) +{ + if (!spi->max_speed_hz) + return -EINVAL; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + return dspi_setup_transfer(spi, NULL); +} + +static irqreturn_t dspi_interrupt(int irq, void *dev_id) +{ + struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; + + writel(SPI_SR_EOQF, dspi->base + SPI_SR); + + dspi_transfer_read(dspi); + + if (!dspi->len) { + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) + set_bit_mode(dspi, 16); + dspi->waitflags = 1; + wake_up_interruptible(&dspi->waitq); + } else { + dspi_transfer_write(dspi); + + return IRQ_HANDLED; + } + + return IRQ_HANDLED; +} + +static struct of_device_id fsl_dspi_dt_ids[] = { + { .compatible = "fsl,vf610-dspi", .data = NULL, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); + +#ifdef CONFIG_PM_SLEEP +static int dspi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_master_get_devdata(master); + + spi_master_suspend(master); + clk_disable_unprepare(dspi->clk); + + return 0; +} + +static int dspi_resume(struct device *dev) +{ + + struct spi_master *master = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_master_get_devdata(master); + + clk_prepare_enable(dspi->clk); + spi_master_resume(master); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops dspi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) +}; + +static int dspi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct fsl_dspi *dspi; + struct resource *res; + int ret = 0, cs_num, bus_num; + + master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); + if (!master) + return -ENOMEM; + + dspi = spi_master_get_devdata(master); + dspi->pdev = pdev; + dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.chipselect = dspi_chipselect; + dspi->bitbang.setup_transfer = dspi_setup_transfer; + dspi->bitbang.txrx_bufs = dspi_txrx_transfer; + dspi->bitbang.master->setup = dspi_setup; + dspi->bitbang.master->dev.of_node = pdev->dev.of_node; + + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | + SPI_BPW_MASK(16); + + ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num); + if (ret < 0) { + dev_err(&pdev->dev, "can't get spi-num-chipselects\n"); + goto out_master_put; + } + master->num_chipselect = cs_num; + + ret = of_property_read_u32(np, "bus-num", &bus_num); + if (ret < 0) { + dev_err(&pdev->dev, "can't get bus-num\n"); + goto out_master_put; + } + master->bus_num = bus_num; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get platform resource\n"); + ret = -EINVAL; + goto out_master_put; + } + + dspi->base = devm_ioremap_resource(&pdev->dev, res); + if (!dspi->base) { + ret = -EINVAL; + goto out_master_put; + } + + dspi->irq = platform_get_irq(pdev, 0); + if (dspi->irq < 0) { + dev_err(&pdev->dev, "can't get platform irq\n"); + ret = dspi->irq; + goto out_master_put; + } + + ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, + pdev->name, dspi); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); + goto out_master_put; + } + + dspi->clk = devm_clk_get(&pdev->dev, "dspi"); + if (IS_ERR(dspi->clk)) { + ret = PTR_ERR(dspi->clk); + dev_err(&pdev->dev, "unable to get clock\n"); + goto out_master_put; + } + clk_prepare_enable(dspi->clk); + + init_waitqueue_head(&dspi->waitq); + platform_set_drvdata(pdev, dspi); + + ret = spi_bitbang_start(&dspi->bitbang); + if (ret != 0) { + dev_err(&pdev->dev, "Problem registering DSPI master\n"); + goto out_clk_put; + } + + pr_info(KERN_INFO "Freescale DSPI master initialized\n"); + return ret; + +out_clk_put: + clk_disable_unprepare(dspi->clk); +out_master_put: + spi_master_put(master); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int dspi_remove(struct platform_device *pdev) +{ + struct fsl_dspi *dspi = platform_get_drvdata(pdev); + + /* Disconnect from the SPI framework */ + spi_bitbang_stop(&dspi->bitbang); + spi_master_put(dspi->bitbang.master); + + return 0; +} + +static struct platform_driver fsl_dspi_driver = { + .driver.name = DRIVER_NAME, + .driver.of_match_table = fsl_dspi_dt_ids, + .driver.owner = THIS_MODULE, + .driver.pm = &dspi_pm, + .probe = dspi_probe, + .remove = dspi_remove, +}; +module_platform_driver(fsl_dspi_driver); + +MODULE_DESCRIPTION("Freescale DSPI Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6a74d78..b8f1103 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -584,7 +584,7 @@ static void fsl_espi_remove(struct mpc8xxx_spi *mspi) static struct spi_master * fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_espi_reg *reg_base; @@ -665,7 +665,7 @@ err: static int of_fsl_espi_get_chipselects(struct device *dev) { struct device_node *np = dev->of_node; - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); const u32 *prop; int len; diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index e947f2d..0b75f26 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -122,7 +122,7 @@ const char *mpc8xxx_spi_strmode(unsigned int flags) int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; int ret = 0; diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 41e89c3..bbc9429 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -574,7 +574,7 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) static void fsl_spi_grlib_probe(struct device *dev) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base; @@ -600,7 +600,7 @@ static void fsl_spi_grlib_probe(struct device *dev) static struct spi_master * fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_spi_reg *reg_base; @@ -700,7 +700,8 @@ err: static void fsl_spi_cs_control(struct spi_device *spi, bool on) { struct device *dev = spi->dev.parent->parent; - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data); + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); u16 cs = spi->chip_select; int gpio = pinfo->gpios[cs]; bool alow = pinfo->alow_flags[cs]; @@ -711,7 +712,7 @@ static void fsl_spi_cs_control(struct spi_device *spi, bool on) static int of_fsl_spi_get_chipselects(struct device *dev) { struct device_node *np = dev->of_node; - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); int ngpios; int i = 0; @@ -790,7 +791,7 @@ err_alloc_flags: static int of_fsl_spi_free_chipselects(struct device *dev) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); int i; @@ -889,7 +890,7 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev) int irq; struct spi_master *master; - if (!pdev->dev.platform_data) + if (!dev_get_platdata(&pdev->dev)) return -EINVAL; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index a54524c..68b69fe 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -420,7 +420,7 @@ static int spi_gpio_probe(struct platform_device *pdev) if (status > 0) use_of = 1; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); #ifdef GENERIC_BITBANG if (!pdata || !pdata->num_chipselect) return -ENODEV; @@ -506,7 +506,7 @@ static int spi_gpio_remove(struct platform_device *pdev) int status; spi_gpio = platform_get_drvdata(pdev); - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* stop() unregisters child devices too */ status = spi_bitbang_stop(&spi_gpio->bitbang); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 7db4f43..15323d8 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -619,6 +619,7 @@ static const struct of_device_id spi_imx_dt_ids[] = { { .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, spi_imx_dt_ids); static void spi_imx_chipselect(struct spi_device *spi, int is_active) { @@ -796,10 +797,11 @@ static int spi_imx_probe(struct platform_device *pdev) if (!gpio_is_valid(cs_gpio)) continue; - ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); + ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i], + DRIVER_NAME); if (ret) { dev_err(&pdev->dev, "can't get cs gpios\n"); - goto out_gpio_free; + goto out_master_put; } } @@ -816,50 +818,44 @@ static int spi_imx_probe(struct platform_device *pdev) (struct spi_imx_devtype_data *) pdev->id_entry->driver_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get platform resource\n"); - ret = -ENOMEM; - goto out_gpio_free; - } - - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - ret = -EBUSY; - goto out_gpio_free; - } - - spi_imx->base = ioremap(res->start, resource_size(res)); - if (!spi_imx->base) { - ret = -EINVAL; - goto out_release_mem; + spi_imx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spi_imx->base)) { + ret = PTR_ERR(spi_imx->base); + goto out_master_put; } spi_imx->irq = platform_get_irq(pdev, 0); if (spi_imx->irq < 0) { ret = -EINVAL; - goto out_iounmap; + goto out_master_put; } - ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx); + ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0, + DRIVER_NAME, spi_imx); if (ret) { dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); - goto out_iounmap; + goto out_master_put; } spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(spi_imx->clk_ipg)) { ret = PTR_ERR(spi_imx->clk_ipg); - goto out_free_irq; + goto out_master_put; } spi_imx->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(spi_imx->clk_per)) { ret = PTR_ERR(spi_imx->clk_per); - goto out_free_irq; + goto out_master_put; } - clk_prepare_enable(spi_imx->clk_per); - clk_prepare_enable(spi_imx->clk_ipg); + ret = clk_prepare_enable(spi_imx->clk_per); + if (ret) + goto out_master_put; + + ret = clk_prepare_enable(spi_imx->clk_ipg); + if (ret) + goto out_put_per; spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per); @@ -879,47 +875,27 @@ static int spi_imx_probe(struct platform_device *pdev) return ret; out_clk_put: - clk_disable_unprepare(spi_imx->clk_per); clk_disable_unprepare(spi_imx->clk_ipg); -out_free_irq: - free_irq(spi_imx->irq, spi_imx); -out_iounmap: - iounmap(spi_imx->base); -out_release_mem: - release_mem_region(res->start, resource_size(res)); -out_gpio_free: - while (--i >= 0) { - if (gpio_is_valid(spi_imx->chipselect[i])) - gpio_free(spi_imx->chipselect[i]); - } +out_put_per: + clk_disable_unprepare(spi_imx->clk_per); +out_master_put: spi_master_put(master); - kfree(master); + return ret; } static int spi_imx_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct spi_imx_data *spi_imx = spi_master_get_devdata(master); - int i; spi_bitbang_stop(&spi_imx->bitbang); writel(0, spi_imx->base + MXC_CSPICTRL); - clk_disable_unprepare(spi_imx->clk_per); clk_disable_unprepare(spi_imx->clk_ipg); - free_irq(spi_imx->irq, spi_imx); - iounmap(spi_imx->base); - - for (i = 0; i < master->num_chipselect; i++) - if (gpio_is_valid(spi_imx->chipselect[i])) - gpio_free(spi_imx->chipselect[i]); - + clk_disable_unprepare(spi_imx->clk_per); spi_master_put(master); - release_mem_region(res->start, resource_size(res)); - return 0; } diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 29fce6a..dbc5e99 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -38,7 +38,8 @@ struct mpc512x_psc_spi { struct mpc512x_psc_fifo __iomem *fifo; unsigned int irq; u8 bits_per_word; - u32 mclk; + struct clk *clk_mclk; + u32 mclk_rate; struct completion txisrdone; }; @@ -72,6 +73,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) struct mpc52xx_psc __iomem *psc = mps->psc; u32 sicr; u32 ccr; + int speed; u16 bclkdiv; sicr = in_be32(&psc->sicr); @@ -95,10 +97,10 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) ccr = in_be32(&psc->ccr); ccr &= 0xFF000000; - if (cs->speed_hz) - bclkdiv = (mps->mclk / cs->speed_hz) - 1; - else - bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */ + speed = cs->speed_hz; + if (!speed) + speed = 1000000; /* default 1MHz */ + bclkdiv = (mps->mclk_rate / speed) - 1; ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8)); out_be32(&psc->ccr, ccr); @@ -386,19 +388,11 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master, { struct mpc52xx_psc __iomem *psc = mps->psc; struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; - struct clk *spiclk; - int ret = 0; - char name[32]; u32 sicr; u32 ccr; + int speed; u16 bclkdiv; - sprintf(name, "psc%d_mclk", master->bus_num); - spiclk = clk_get(&master->dev, name); - clk_enable(spiclk); - mps->mclk = clk_get_rate(spiclk); - clk_put(spiclk); - /* Reset the PSC into a known state */ out_8(&psc->command, MPC52xx_PSC_RST_RX); out_8(&psc->command, MPC52xx_PSC_RST_TX); @@ -425,7 +419,8 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master, ccr = in_be32(&psc->ccr); ccr &= 0xFF000000; - bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */ + speed = 1000000; /* default 1MHz */ + bclkdiv = (mps->mclk_rate / speed) - 1; ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8)); out_be32(&psc->ccr, ccr); @@ -445,7 +440,7 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master, mps->bits_per_word = 8; - return ret; + return 0; } static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id) @@ -474,11 +469,14 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, s16 bus_num) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc512x_psc_spi *mps; struct spi_master *master; int ret; void *tempp; + int psc_num; + char clk_name[16]; + struct clk *clk; master = spi_alloc_master(dev, sizeof *mps); if (master == NULL) @@ -521,16 +519,29 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, goto free_master; init_completion(&mps->txisrdone); + psc_num = master->bus_num; + snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num); + clk = devm_clk_get(dev, clk_name); + if (IS_ERR(clk)) + goto free_irq; + ret = clk_prepare_enable(clk); + if (ret) + goto free_irq; + mps->clk_mclk = clk; + mps->mclk_rate = clk_get_rate(clk); + ret = mpc512x_psc_spi_port_config(master, mps); if (ret < 0) - goto free_irq; + goto free_clock; ret = spi_register_master(master); if (ret < 0) - goto free_irq; + goto free_clock; return ret; +free_clock: + clk_disable_unprepare(mps->clk_mclk); free_irq: free_irq(mps->irq, mps); free_master: @@ -547,6 +558,7 @@ static int mpc512x_psc_spi_do_remove(struct device *dev) struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); spi_unregister_master(master); + clk_disable_unprepare(mps->clk_mclk); free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index fed0571..6e925dc 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -366,7 +366,7 @@ static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, s16 bus_num) { - struct fsl_spi_platform_data *pdata = dev->platform_data; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc52xx_psc_spi *mps; struct spi_master *master; int ret; diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 424d38e..de7b114 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -67,13 +67,8 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, { struct mxs_spi *spi = spi_master_get_devdata(dev->master); struct mxs_ssp *ssp = &spi->ssp; - uint8_t bits_per_word; uint32_t hz = 0; - bits_per_word = dev->bits_per_word; - if (t && t->bits_per_word) - bits_per_word = t->bits_per_word; - hz = dev->max_speed_hz; if (t && t->speed_hz) hz = min(hz, t->speed_hz); @@ -513,7 +508,7 @@ static int mxs_spi_probe(struct platform_device *pdev) iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); - if (!iores || irq_err < 0) + if (irq_err < 0) return -EINVAL; base = devm_ioremap_resource(&pdev->dev, iores); @@ -563,25 +558,31 @@ static int mxs_spi_probe(struct platform_device *pdev) goto out_master_free; } - clk_prepare_enable(ssp->clk); + ret = clk_prepare_enable(ssp->clk); + if (ret) + goto out_dma_release; + clk_set_rate(ssp->clk, clk_freq); ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; - stmp_reset_block(ssp->base); + ret = stmp_reset_block(ssp->base); + if (ret) + goto out_disable_clk; platform_set_drvdata(pdev, master); ret = spi_register_master(master); if (ret) { dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); - goto out_free_dma; + goto out_disable_clk; } return 0; -out_free_dma: - dma_release_channel(ssp->dmach); +out_disable_clk: clk_disable_unprepare(ssp->clk); +out_dma_release: + dma_release_channel(ssp->dmach); out_master_free: spi_master_put(master); return ret; @@ -598,11 +599,8 @@ static int mxs_spi_remove(struct platform_device *pdev) ssp = &spi->ssp; spi_unregister_master(master); - - dma_release_channel(ssp->dmach); - clk_disable_unprepare(ssp->clk); - + dma_release_channel(ssp->dmach); spi_master_put(master); return 0; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index 150d854..47a68b4 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -174,17 +174,6 @@ static void nuc900_spi_gobusy(struct nuc900_spi *hw) spin_unlock_irqrestore(&hw->lock, flags); } -static int nuc900_spi_setupxfer(struct spi_device *spi, - struct spi_transfer *t) -{ - return 0; -} - -static int nuc900_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -361,7 +350,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); hw->master = spi_master_get(master); - hw->pdata = pdev->dev.platform_data; + hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; if (hw->pdata == NULL) { @@ -373,14 +362,12 @@ static int nuc900_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); init_completion(&hw->done); - master->mode_bits = SPI_MODE_0; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->num_chipselect = hw->pdata->num_cs; master->bus_num = hw->pdata->bus_num; hw->bitbang.master = hw->master; - hw->bitbang.setup_transfer = nuc900_spi_setupxfer; hw->bitbang.chipselect = nuc900_spi_chipsel; hw->bitbang.txrx_bufs = nuc900_spi_txrx; - hw->bitbang.master->setup = nuc900_spi_setup; hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (hw->res == NULL) { diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 58deb79..333cb1b 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -285,7 +285,7 @@ static int tiny_spi_of_probe(struct platform_device *pdev) static int tiny_spi_probe(struct platform_device *pdev) { - struct tiny_spi_platform_data *platp = pdev->dev.platform_data; + struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev); struct tiny_spi *hw; struct spi_master *master; struct resource *res; @@ -315,15 +315,11 @@ static int tiny_spi_probe(struct platform_device *pdev) /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto exit_busy; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) - goto exit_busy; - hw->base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!hw->base) - goto exit_busy; + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) { + err = PTR_ERR(hw->base); + goto exit; + } /* irq is optional */ hw->irq = platform_get_irq(pdev, 0); if (hw->irq >= 0) { @@ -337,8 +333,10 @@ static int tiny_spi_probe(struct platform_device *pdev) if (platp) { hw->gpio_cs_count = platp->gpio_cs_count; hw->gpio_cs = platp->gpio_cs; - if (platp->gpio_cs_count && !platp->gpio_cs) - goto exit_busy; + if (platp->gpio_cs_count && !platp->gpio_cs) { + err = -EBUSY; + goto exit; + } hw->freq = platp->freq; hw->baudwidth = platp->baudwidth; } else { @@ -365,8 +363,6 @@ static int tiny_spi_probe(struct platform_device *pdev) exit_gpio: while (i-- > 0) gpio_free(hw->gpio_cs[i]); -exit_busy: - err = -EBUSY; exit: spi_master_put(master); return err; diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 24daf96..5f28ddb 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -28,7 +28,6 @@ #define OCTEON_SPI_MAX_CLOCK_HZ 16000000 struct octeon_spi { - struct spi_master *my_master; u64 register_base; u64 last_cfg; u64 cs_enax; @@ -64,7 +63,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, unsigned int speed_hz; int mode; bool cpha, cpol; - int bits_per_word; const u8 *tx_buf; u8 *rx_buf; int len; @@ -76,12 +74,9 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mode = msg_setup->mode; cpha = mode & SPI_CPHA; cpol = mode & SPI_CPOL; - bits_per_word = msg_setup->bits_per_word; if (xfer->speed_hz) speed_hz = xfer->speed_hz; - if (xfer->bits_per_word) - bits_per_word = xfer->bits_per_word; if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ) speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; @@ -166,19 +161,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, return xfer->len; } -static int octeon_spi_validate_bpw(struct spi_device *spi, u32 speed) -{ - switch (speed) { - case 8: - break; - default: - dev_err(&spi->dev, "Error: %d bits per word not supported\n", - speed); - return -EINVAL; - } - return 0; -} - static int octeon_spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { @@ -197,15 +179,6 @@ static int octeon_spi_transfer_one_message(struct spi_master *master, } list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (xfer->bits_per_word) { - status = octeon_spi_validate_bpw(msg->spi, - xfer->bits_per_word); - if (status) - goto err; - } - } - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { bool last_xfer = &xfer->transfer_list == msg->transfers.prev; int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); if (r < 0) { @@ -236,14 +209,9 @@ static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi) static int octeon_spi_setup(struct spi_device *spi) { - int r; struct octeon_spi_setup *new_setup; struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); - r = octeon_spi_validate_bpw(spi, spi->bits_per_word); - if (r) - return r; - new_setup = octeon_spi_new_setup(spi); if (!new_setup) return -ENOMEM; @@ -261,14 +229,8 @@ static void octeon_spi_cleanup(struct spi_device *spi) kfree(old_setup); } -static int octeon_spi_nop_transfer_hardware(struct spi_master *master) -{ - return 0; -} - static int octeon_spi_probe(struct platform_device *pdev) { - struct resource *res_mem; struct spi_master *master; struct octeon_spi *p; @@ -278,8 +240,7 @@ static int octeon_spi_probe(struct platform_device *pdev) if (!master) return -ENOMEM; p = spi_master_get_devdata(master); - platform_set_drvdata(pdev, p); - p->my_master = master; + platform_set_drvdata(pdev, master); res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -307,9 +268,8 @@ static int octeon_spi_probe(struct platform_device *pdev) master->setup = octeon_spi_setup; master->cleanup = octeon_spi_cleanup; - master->prepare_transfer_hardware = octeon_spi_nop_transfer_hardware; master->transfer_one_message = octeon_spi_transfer_one_message; - master->unprepare_transfer_hardware = octeon_spi_nop_transfer_hardware; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; err = spi_register_master(master); @@ -328,10 +288,11 @@ fail: static int octeon_spi_remove(struct platform_device *pdev) { - struct octeon_spi *p = platform_get_drvdata(pdev); + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); u64 register_base = p->register_base; - spi_unregister_master(p->my_master); + spi_unregister_master(master); /* Clear the CSENA* and put everything in a known state. */ cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index ee25670..69ecf05 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -83,11 +83,6 @@ #define SPI_SHUTDOWN 1 struct omap1_spi100k { - struct work_struct work; - - /* lock protects queue and registers */ - spinlock_t lock; - struct list_head msg_queue; struct spi_master *master; struct clk *ick; struct clk *fck; @@ -104,8 +99,6 @@ struct omap1_spi100k_cs { int word_len; }; -static struct workqueue_struct *omap1_spi100k_wq; - #define MOD_REG_BIT(val, mask, set) do { \ if (set) \ val |= mask; \ @@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi) spi100k_open(spi->master); - clk_enable(spi100k->ick); - clk_enable(spi100k->fck); + clk_prepare_enable(spi100k->ick); + clk_prepare_enable(spi100k->fck); ret = omap1_spi100k_setup_transfer(spi, NULL); - clk_disable(spi100k->ick); - clk_disable(spi100k->fck); + clk_disable_unprepare(spi100k->ick); + clk_disable_unprepare(spi100k->fck); return ret; } -static void omap1_spi100k_work(struct work_struct *work) +static int omap1_spi100k_prepare_hardware(struct spi_master *master) { - struct omap1_spi100k *spi100k; - int status = 0; + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); - spi100k = container_of(work, struct omap1_spi100k, work); - spin_lock_irq(&spi100k->lock); + clk_prepare_enable(spi100k->ick); + clk_prepare_enable(spi100k->fck); - clk_enable(spi100k->ick); - clk_enable(spi100k->fck); + return 0; +} - /* We only enable one channel at a time -- the one whose message is - * at the head of the queue -- although this controller would gladly - * arbitrate among multiple channels. This corresponds to "single - * channel" master mode. As a side effect, we need to manage the - * chipselect with the FORCE bit ... CS != channel enable. - */ - while (!list_empty(&spi100k->msg_queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int cs_active = 0; - struct omap1_spi100k_cs *cs; - int par_override = 0; - - m = container_of(spi100k->msg_queue.next, struct spi_message, - queue); - - list_del_init(&m->queue); - spin_unlock_irq(&spi100k->lock); - - spi = m->spi; - cs = spi->controller_state; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - status = -EINVAL; +static int omap1_spi100k_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t = NULL; + int cs_active = 0; + int par_override = 0; + int status = 0; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + status = -EINVAL; + break; + } + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = omap1_spi100k_setup_transfer(spi, t); + if (status < 0) break; - } - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = omap1_spi100k_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; + } - if (!cs_active) { - omap1_spi100k_force_cs(spi100k, 1); - cs_active = 1; - } + if (!cs_active) { + omap1_spi100k_force_cs(spi100k, 1); + cs_active = 1; + } - if (t->len) { - unsigned count; + if (t->len) { + unsigned count; - count = omap1_spi100k_txrx_pio(spi, t); - m->actual_length += count; + count = omap1_spi100k_txrx_pio(spi, t); + m->actual_length += count; - if (count != t->len) { - status = -EIO; - break; - } + if (count != t->len) { + status = -EIO; + break; } + } - if (t->delay_usecs) - udelay(t->delay_usecs); + if (t->delay_usecs) + udelay(t->delay_usecs); - /* ignore the "leave it on after last xfer" hint */ + /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap1_spi100k_force_cs(spi100k, 0); - cs_active = 0; - } - } - - /* Restore defaults if they were overriden */ - if (par_override) { - par_override = 0; - status = omap1_spi100k_setup_transfer(spi, NULL); + if (t->cs_change) { + omap1_spi100k_force_cs(spi100k, 0); + cs_active = 0; } + } - if (cs_active) - omap1_spi100k_force_cs(spi100k, 0); + /* Restore defaults if they were overriden */ + if (par_override) { + par_override = 0; + status = omap1_spi100k_setup_transfer(spi, NULL); + } - m->status = status; - m->complete(m->context); + if (cs_active) + omap1_spi100k_force_cs(spi100k, 0); - spin_lock_irq(&spi100k->lock); - } + m->status = status; - clk_disable(spi100k->ick); - clk_disable(spi100k->fck); - spin_unlock_irq(&spi100k->lock); + spi_finalize_current_message(master); - if (status < 0) - printk(KERN_WARNING "spi transfer failed with %d\n", status); + return status; } -static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) +static int omap1_spi100k_unprepare_hardware(struct spi_master *master) { - struct omap1_spi100k *spi100k; - unsigned long flags; - struct spi_transfer *t; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - spi100k = spi_master_get_devdata(spi->master); - - /* Don't accept new work if we're shutting down */ - if (spi100k->state == SPI_SHUTDOWN) - return -ESHUTDOWN; - - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - list_for_each_entry(t, &m->transfers, transfer_list) { - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ - || (len && !(rx_buf || tx_buf))) { - dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - return -EINVAL; - } - - if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) { - dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", - t->speed_hz, - OMAP1_SPI100K_MAX_FREQ/(1<<16)); - return -EINVAL; - } - - } - - spin_lock_irqsave(&spi100k->lock, flags); - list_add_tail(&m->queue, &spi100k->msg_queue); - queue_work(omap1_spi100k_wq, &spi100k->work); - spin_unlock_irqrestore(&spi100k->lock, flags); + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); - return 0; -} + clk_disable_unprepare(spi100k->ick); + clk_disable_unprepare(spi100k->fck); -static int omap1_spi100k_reset(struct omap1_spi100k *spi100k) -{ return 0; } @@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->setup = omap1_spi100k_setup; - master->transfer = omap1_spi100k_transfer; + master->transfer_one_message = omap1_spi100k_transfer_one_message; + master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; + master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; master->cleanup = NULL; master->num_chipselect = 2; master->mode_bits = MODEBITS; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); + master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ; platform_set_drvdata(pdev, master); @@ -512,42 +441,31 @@ static int omap1_spi100k_probe(struct platform_device *pdev) * You should allocate this with ioremap() before initializing * the SPI. */ - spi100k->base = (void __iomem *) pdev->dev.platform_data; - - INIT_WORK(&spi100k->work, omap1_spi100k_work); + spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev); - spin_lock_init(&spi100k->lock); - INIT_LIST_HEAD(&spi100k->msg_queue); - spi100k->ick = clk_get(&pdev->dev, "ick"); + spi100k->ick = devm_clk_get(&pdev->dev, "ick"); if (IS_ERR(spi100k->ick)) { dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); status = PTR_ERR(spi100k->ick); - goto err1; + goto err; } - spi100k->fck = clk_get(&pdev->dev, "fck"); + spi100k->fck = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(spi100k->fck)) { dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); status = PTR_ERR(spi100k->fck); - goto err2; + goto err; } - if (omap1_spi100k_reset(spi100k) < 0) - goto err3; - status = spi_register_master(master); if (status < 0) - goto err3; + goto err; spi100k->state = SPI_RUNNING; return status; -err3: - clk_put(spi100k->fck); -err2: - clk_put(spi100k->ick); -err1: +err: spi_master_put(master); return status; } @@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev) struct spi_master *master; struct omap1_spi100k *spi100k; struct resource *r; - unsigned limit = 500; - unsigned long flags; int status = 0; master = platform_get_drvdata(pdev); spi100k = spi_master_get_devdata(master); - spin_lock_irqsave(&spi100k->lock, flags); - - spi100k->state = SPI_SHUTDOWN; - while (!list_empty(&spi100k->msg_queue) && limit--) { - spin_unlock_irqrestore(&spi100k->lock, flags); - msleep(10); - spin_lock_irqsave(&spi100k->lock, flags); - } - - if (!list_empty(&spi100k->msg_queue)) - status = -EBUSY; - - spin_unlock_irqrestore(&spi100k->lock, flags); - if (status != 0) return status; - clk_put(spi100k->fck); - clk_put(spi100k->ick); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi_unregister_master(master); @@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = { .name = "omap1_spi100k", .owner = THIS_MODULE, }, + .probe = omap1_spi100k_probe, .remove = omap1_spi100k_remove, }; - -static int __init omap1_spi100k_init(void) -{ - omap1_spi100k_wq = create_singlethread_workqueue( - omap1_spi100k_driver.driver.name); - - if (omap1_spi100k_wq == NULL) - return -1; - - return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); -} - -static void __exit omap1_spi100k_exit(void) -{ - platform_driver_unregister(&omap1_spi100k_driver); - - destroy_workqueue(omap1_spi100k_wq); -} - -module_init(omap1_spi100k_init); -module_exit(omap1_spi100k_exit); +module_platform_driver(omap1_spi100k_driver); MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 5994039..ed4af47 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -335,23 +335,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } -static int omap2_prepare_transfer(struct spi_master *master) -{ - struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - - pm_runtime_get_sync(mcspi->dev); - return 0; -} - -static int omap2_unprepare_transfer(struct spi_master *master) -{ - struct omap2_mcspi *mcspi = spi_master_get_devdata(master); - - pm_runtime_mark_last_busy(mcspi->dev); - pm_runtime_put_autosuspend(mcspi->dev); - return 0; -} - static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) { unsigned long timeout; @@ -1318,8 +1301,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; - master->prepare_transfer_hardware = omap2_prepare_transfer; - master->unprepare_transfer_hardware = omap2_unprepare_transfer; + master->auto_runtime_pm = true; master->transfer_one_message = omap2_mcspi_transfer_one_message; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; @@ -1340,7 +1322,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL)) mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; } else { - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); master->num_chipselect = pdata->num_cs; if (pdev->id != -1) master->bus_num = pdev->id; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 5d90beb..1d1d321 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/clk.h> +#include <linux/sizes.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" @@ -446,30 +447,22 @@ static int orion_spi_probe(struct platform_device *pdev) spi->min_speed = DIV_ROUND_UP(tclk_hz, 30); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - status = -ENODEV; + spi->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(spi->base)) { + status = PTR_ERR(spi->base); goto out_rel_clk; } - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - status = -EBUSY; - goto out_rel_clk; - } - spi->base = ioremap(r->start, SZ_1K); - if (orion_spi_reset(spi) < 0) - goto out_rel_mem; + goto out_rel_clk; master->dev.of_node = pdev->dev.of_node; status = spi_register_master(master); if (status < 0) - goto out_rel_mem; + goto out_rel_clk; return status; -out_rel_mem: - release_mem_region(r->start, resource_size(r)); out_rel_clk: clk_disable_unprepare(spi->clk); clk_put(spi->clk); @@ -482,7 +475,6 @@ out: static int orion_spi_remove(struct platform_device *pdev) { struct spi_master *master; - struct resource *r; struct orion_spi *spi; master = platform_get_drvdata(pdev); @@ -491,9 +483,6 @@ static int orion_spi_remove(struct platform_device *pdev) clk_disable_unprepare(spi->clk); clk_put(spi->clk); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - spi_unregister_master(master); return 0; diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index abef061..9c511a9 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1555,18 +1555,6 @@ static int pl022_transfer_one_message(struct spi_master *master, return 0; } -static int pl022_prepare_transfer_hardware(struct spi_master *master) -{ - struct pl022 *pl022 = spi_master_get_devdata(master); - - /* - * Just make sure we have all we need to run the transfer by syncing - * with the runtime PM framework. - */ - pm_runtime_get_sync(&pl022->adev->dev); - return 0; -} - static int pl022_unprepare_transfer_hardware(struct spi_master *master) { struct pl022 *pl022 = spi_master_get_devdata(master); @@ -1575,13 +1563,6 @@ static int pl022_unprepare_transfer_hardware(struct spi_master *master) writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); - if (pl022->master_info->autosuspend_delay > 0) { - pm_runtime_mark_last_busy(&pl022->adev->dev); - pm_runtime_put_autosuspend(&pl022->adev->dev); - } else { - pm_runtime_put(&pl022->adev->dev); - } - return 0; } @@ -2091,7 +2072,8 @@ pl022_platform_data_dt_get(struct device *dev) static int pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; - struct pl022_ssp_controller *platform_info = adev->dev.platform_data; + struct pl022_ssp_controller *platform_info = + dev_get_platdata(&adev->dev); struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ struct device_node *np = adev->dev.of_node; @@ -2139,7 +2121,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) master->num_chipselect = num_cs; master->cleanup = pl022_cleanup; master->setup = pl022_setup; - master->prepare_transfer_hardware = pl022_prepare_transfer_hardware; + master->auto_runtime_pm = true; master->transfer_one_message = pl022_transfer_one_message; master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; @@ -2193,8 +2175,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) status = -ENOMEM; goto err_no_ioremap; } - printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n", - adev->res.start, pl022->virtbase); + printk(KERN_INFO "pl022: mapped registers from %pa to %p\n", + &adev->res.start, pl022->virtbase); pl022->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(pl022->clk)) { diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f440dce..2eb06ee 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -69,6 +69,8 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_TX_HITHRESH_DFLT 224 /* Offset from drv_data->lpss_base */ +#define GENERAL_REG 0x08 +#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) #define SSP_REG 0x0c #define SPI_CS_CONTROL 0x18 #define SPI_CS_CONTROL_SW_MODE BIT(0) @@ -142,8 +144,13 @@ detection_done: __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); /* Enable multiblock DMA transfers */ - if (drv_data->master_info->enable_dma) + if (drv_data->master_info->enable_dma) { __lpss_ssp_write_priv(drv_data, SSP_REG, 1); + + value = __lpss_ssp_read_priv(drv_data, GENERAL_REG); + value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; + __lpss_ssp_write_priv(drv_data, GENERAL_REG, value); + } } static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) @@ -804,14 +811,6 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master, return 0; } -static int pxa2xx_spi_prepare_transfer(struct spi_master *master) -{ - struct driver_data *drv_data = spi_master_get_devdata(master); - - pm_runtime_get_sync(&drv_data->pdev->dev); - return 0; -} - static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) { struct driver_data *drv_data = spi_master_get_devdata(master); @@ -820,8 +819,6 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE, drv_data->ioaddr); - pm_runtime_mark_last_busy(&drv_data->pdev->dev); - pm_runtime_put_autosuspend(&drv_data->pdev->dev); return 0; } @@ -1134,8 +1131,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->cleanup = cleanup; master->setup = setup; master->transfer_one_message = pxa2xx_spi_transfer_one_message; - master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; + master->auto_runtime_pm = true; drv_data->ssp_type = ssp->type; drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index b44a6ac..8719206 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -564,8 +564,12 @@ static void rspi_work(struct work_struct *work) unsigned long flags; int ret; - spin_lock_irqsave(&rspi->lock, flags); - while (!list_empty(&rspi->queue)) { + while (1) { + spin_lock_irqsave(&rspi->lock, flags); + if (list_empty(&rspi->queue)) { + spin_unlock_irqrestore(&rspi->lock, flags); + break; + } mesg = list_entry(rspi->queue.next, struct spi_message, queue); list_del_init(&mesg->queue); spin_unlock_irqrestore(&rspi->lock, flags); @@ -595,8 +599,6 @@ static void rspi_work(struct work_struct *work) mesg->status = 0; mesg->complete(mesg->context); - - spin_lock_irqsave(&rspi->lock, flags); } return; @@ -664,12 +666,13 @@ static irqreturn_t rspi_irq(int irq, void *_sr) static int rspi_request_dma(struct rspi_data *rspi, struct platform_device *pdev) { - struct rspi_plat_data *rspi_pd = pdev->dev.platform_data; + struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dma_cap_mask_t mask; struct dma_slave_config cfg; int ret; - if (!rspi_pd) + if (!res || !rspi_pd) return 0; /* The driver assumes no error. */ rspi->dma_width_16bit = rspi_pd->dma_width_16bit; @@ -683,6 +686,8 @@ static int rspi_request_dma(struct rspi_data *rspi, if (rspi->chan_rx) { cfg.slave_id = rspi_pd->dma_rx_id; cfg.direction = DMA_DEV_TO_MEM; + cfg.dst_addr = 0; + cfg.src_addr = res->start + RSPI_SPDR; ret = dmaengine_slave_config(rspi->chan_rx, &cfg); if (!ret) dev_info(&pdev->dev, "Use DMA when rx.\n"); @@ -698,6 +703,8 @@ static int rspi_request_dma(struct rspi_data *rspi, if (rspi->chan_tx) { cfg.slave_id = rspi_pd->dma_tx_id; cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = res->start + RSPI_SPDR; + cfg.src_addr = 0; ret = dmaengine_slave_config(rspi->chan_tx, &cfg); if (!ret) dev_info(&pdev->dev, "Use DMA when tx\n"); @@ -719,7 +726,7 @@ static void rspi_release_dma(struct rspi_data *rspi) static int rspi_remove(struct platform_device *pdev) { - struct rspi_data *rspi = platform_get_drvdata(pdev); + struct rspi_data *rspi = spi_master_get(platform_get_drvdata(pdev)); spi_unregister_master(rspi->master); rspi_release_dma(rspi); diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 68910b3..ce318d9 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -525,7 +525,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) memset(hw, 0, sizeof(struct s3c24xx_spi)); hw->master = spi_master_get(master); - hw->pdata = pdata = pdev->dev.platform_data; + hw->pdata = pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; if (pdata == NULL) { @@ -690,7 +690,7 @@ static int s3c24xx_spi_remove(struct platform_device *dev) static int s3c24xx_spi_suspend(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); + struct s3c24xx_spi *hw = dev_get_drvdata(dev); if (hw->pdata && hw->pdata->gpio_setup) hw->pdata->gpio_setup(hw->pdata, 0); @@ -701,7 +701,7 @@ static int s3c24xx_spi_suspend(struct device *dev) static int s3c24xx_spi_resume(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); + struct s3c24xx_spi *hw = dev_get_drvdata(dev); s3c24xx_spi_initialsetup(hw); return 0; diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 63e2070..512b889 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -172,7 +172,6 @@ struct s3c64xx_spi_port_config { * @master: Pointer to the SPI Protocol master. * @cntrlr_info: Platform specific data for the controller this driver manages. * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint. - * @queue: To log SPI xfer requests. * @lock: Controller specific lock. * @state: Set of FLAGS to indicate status. * @rx_dmach: Controller's DMA channel for Rx. @@ -193,7 +192,6 @@ struct s3c64xx_spi_driver_data { struct spi_master *master; struct s3c64xx_spi_info *cntrlr_info; struct spi_device *tgl_spi; - struct list_head queue; spinlock_t lock; unsigned long sfr_start; struct completion xfer_completion; @@ -338,8 +336,10 @@ static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) req.cap = DMA_SLAVE; req.client = &s3c64xx_spi_dma_client; - sdd->rx_dma.ch = (void *)sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx"); - sdd->tx_dma.ch = (void *)sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx"); + sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( + sdd->rx_dma.dmach, &req, dev, "rx"); + sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( + sdd->tx_dma.dmach, &req, dev, "tx"); return 1; } @@ -356,8 +356,6 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) while (!is_polling(sdd) && !acquire_dma(sdd)) usleep_range(10000, 11000); - pm_runtime_get_sync(&sdd->pdev->dev); - return 0; } @@ -372,7 +370,6 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, &s3c64xx_spi_dma_client); } - pm_runtime_put(&sdd->pdev->dev); return 0; } @@ -389,9 +386,10 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, { struct s3c64xx_spi_driver_data *sdd; struct dma_slave_config config; - struct scatterlist sg; struct dma_async_tx_descriptor *desc; + memset(&config, 0, sizeof(config)); + if (dma->direction == DMA_DEV_TO_MEM) { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, rx_dma); @@ -410,14 +408,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, dmaengine_slave_config(dma->ch, &config); } - sg_init_table(&sg, 1); - sg_dma_len(&sg) = len; - sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)), - len, offset_in_page(buf)); - sg_dma_address(&sg) = buf; - - desc = dmaengine_prep_slave_sg(dma->ch, - &sg, 1, dma->direction, DMA_PREP_INTERRUPT); + desc = dmaengine_prep_slave_single(dma->ch, buf, len, + dma->direction, DMA_PREP_INTERRUPT); desc->callback = s3c64xx_spi_dmacb; desc->callback_param = dma; @@ -434,27 +426,26 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) dma_cap_mask_t mask; int ret; - if (is_polling(sdd)) - return 0; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* Acquire DMA channels */ - sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void*)sdd->rx_dma.dmach, dev, "rx"); - if (!sdd->rx_dma.ch) { - dev_err(dev, "Failed to get RX DMA channel\n"); - ret = -EBUSY; - goto out; - } + if (!is_polling(sdd)) { + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Acquire DMA channels */ + sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, + (void *)sdd->rx_dma.dmach, dev, "rx"); + if (!sdd->rx_dma.ch) { + dev_err(dev, "Failed to get RX DMA channel\n"); + ret = -EBUSY; + goto out; + } - sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void*)sdd->tx_dma.dmach, dev, "tx"); - if (!sdd->tx_dma.ch) { - dev_err(dev, "Failed to get TX DMA channel\n"); - ret = -EBUSY; - goto out_rx; + sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, + (void *)sdd->tx_dma.dmach, dev, "tx"); + if (!sdd->tx_dma.ch) { + dev_err(dev, "Failed to get TX DMA channel\n"); + ret = -EBUSY; + goto out_rx; + } } ret = pm_runtime_get_sync(&sdd->pdev->dev); @@ -1056,8 +1047,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) struct s3c64xx_spi_csinfo *cs = spi->controller_data; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; - struct spi_message *msg; - unsigned long flags; int err; sdd = spi_master_get_devdata(spi->master); @@ -1071,37 +1060,23 @@ static int s3c64xx_spi_setup(struct spi_device *spi) return -ENODEV; } - /* Request gpio only if cs line is asserted by gpio pins */ - if (sdd->cs_gpio) { - err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH, - dev_name(&spi->dev)); - if (err) { - dev_err(&spi->dev, - "Failed to get /CS gpio [%d]: %d\n", - cs->line, err); - goto err_gpio_req; + if (!spi_get_ctldata(spi)) { + /* Request gpio only if cs line is asserted by gpio pins */ + if (sdd->cs_gpio) { + err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (err) { + dev_err(&spi->dev, + "Failed to get /CS gpio [%d]: %d\n", + cs->line, err); + goto err_gpio_req; + } } - } - if (!spi_get_ctldata(spi)) spi_set_ctldata(spi, cs); - - sci = sdd->cntrlr_info; - - spin_lock_irqsave(&sdd->lock, flags); - - list_for_each_entry(msg, &sdd->queue, queue) { - /* Is some mssg is already queued for this device */ - if (msg->spi == spi) { - dev_err(&spi->dev, - "setup: attempt while mssg in queue!\n"); - spin_unlock_irqrestore(&sdd->lock, flags); - err = -EBUSY; - goto err_msgq; - } } - spin_unlock_irqrestore(&sdd->lock, flags); + sci = sdd->cntrlr_info; pm_runtime_get_sync(&sdd->pdev->dev); @@ -1149,7 +1124,6 @@ setup_exit: /* setup() returns with device de-selected */ disable_cs(sdd, spi); -err_msgq: gpio_free(cs->line); spi_set_ctldata(spi, NULL); @@ -1275,7 +1249,7 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) #else static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) { - return dev->platform_data; + return dev_get_platdata(dev); } #endif @@ -1300,7 +1274,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) struct resource *mem_res; struct resource *res; struct s3c64xx_spi_driver_data *sdd; - struct s3c64xx_spi_info *sci = pdev->dev.platform_data; + struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); struct spi_master *master; int ret, irq; char clk_name[16]; @@ -1364,16 +1338,14 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (!sdd->pdev->dev.of_node) { res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { - dev_warn(&pdev->dev, "Unable to get SPI tx dma " - "resource. Switching to poll mode\n"); + dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n"); sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; } else sdd->tx_dma.dmach = res->start; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { - dev_warn(&pdev->dev, "Unable to get SPI rx dma " - "resource. Switching to poll mode\n"); + dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n"); sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; } else sdd->rx_dma.dmach = res->start; @@ -1395,6 +1367,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) SPI_BPW_MASK(8); /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->auto_runtime_pm = true; sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sdd->regs)) { @@ -1442,7 +1415,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) spin_lock_init(&sdd->lock); init_completion(&sdd->xfer_completion); - INIT_LIST_HEAD(&sdd->queue); ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd); @@ -1464,8 +1436,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); - dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", - mem_res->end, mem_res->start, + dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n", + mem_res, sdd->rx_dma.dmach, sdd->tx_dma.dmach); pm_runtime_enable(&pdev->dev); diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 716edf9..0b68cb5 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -99,21 +99,6 @@ static int hspi_status_check_timeout(struct hspi_priv *hspi, u32 mask, u32 val) /* * spi master function */ -static int hspi_prepare_transfer(struct spi_master *master) -{ - struct hspi_priv *hspi = spi_master_get_devdata(master); - - pm_runtime_get_sync(hspi->dev); - return 0; -} - -static int hspi_unprepare_transfer(struct spi_master *master) -{ - struct hspi_priv *hspi = spi_master_get_devdata(master); - - pm_runtime_put_sync(hspi->dev); - return 0; -} #define hspi_hw_cs_enable(hspi) hspi_hw_cs_ctrl(hspi, 0) #define hspi_hw_cs_disable(hspi) hspi_hw_cs_ctrl(hspi, 1) @@ -316,9 +301,8 @@ static int hspi_probe(struct platform_device *pdev) master->setup = hspi_setup; master->cleanup = hspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA; - master->prepare_transfer_hardware = hspi_prepare_transfer; + master->auto_runtime_pm = true; master->transfer_one_message = hspi_transfer_one_message; - master->unprepare_transfer_hardware = hspi_unprepare_transfer; ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); @@ -327,8 +311,6 @@ static int hspi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - dev_info(&pdev->dev, "probed\n"); - return 0; error1: diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 2bc5a6b..2a95435 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -645,7 +645,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) if (pdev->dev.of_node) p->info = sh_msiof_spi_parse_dt(&pdev->dev); else - p->info = pdev->dev.platform_data; + p->info = dev_get_platdata(&pdev->dev); if (!p->info) { dev_err(&pdev->dev, "failed to obtain device info\n"); @@ -745,18 +745,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) return ret; } -static int sh_msiof_spi_runtime_nop(struct device *dev) -{ - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id sh_msiof_match[] = { { .compatible = "renesas,sh-msiof", }, @@ -766,18 +754,12 @@ static const struct of_device_id sh_msiof_match[] = { MODULE_DEVICE_TABLE(of, sh_msiof_match); #endif -static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = { - .runtime_suspend = sh_msiof_spi_runtime_nop, - .runtime_resume = sh_msiof_spi_runtime_nop, -}; - static struct platform_driver sh_msiof_spi_drv = { .probe = sh_msiof_spi_probe, .remove = sh_msiof_spi_remove, .driver = { .name = "spi_sh_msiof", .owner = THIS_MODULE, - .pm = &sh_msiof_spi_dev_pm_ops, .of_match_table = of_match_ptr(sh_msiof_match), }, }; diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 097e5060..8eefeb6 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -130,7 +130,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) sp = spi_master_get_devdata(master); platform_set_drvdata(dev, sp); - sp->info = dev->dev.platform_data; + sp->info = dev_get_platdata(&dev->dev); /* setup spi bitbang adaptor */ sp->bitbang.master = spi_master_get(master); diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index fc20bcf..a1f21b7 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -19,6 +19,10 @@ #include <linux/of_gpio.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/sirfsoc_dma.h> #define DRIVER_NAME "sirfsoc_spi" @@ -119,9 +123,19 @@ #define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20) #define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2) +/* + * only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma + * due to the limitation of dma controller + */ + +#define ALIGNED(x) (!((u32)x & 0x3)) +#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \ + ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE)) + struct sirfsoc_spi { struct spi_bitbang bitbang; - struct completion done; + struct completion rx_done; + struct completion tx_done; void __iomem *base; u32 ctrl_freq; /* SPI controller clock speed */ @@ -137,8 +151,16 @@ struct sirfsoc_spi { void (*tx_word) (struct sirfsoc_spi *); /* number of words left to be tranmitted/received */ - unsigned int left_tx_cnt; - unsigned int left_rx_cnt; + unsigned int left_tx_word; + unsigned int left_rx_word; + + /* rx & tx DMA channels */ + struct dma_chan *rx_chan; + struct dma_chan *tx_chan; + dma_addr_t src_start; + dma_addr_t dst_start; + void *dummypage; + int word_width; /* in bytes */ int chipselect[0]; }; @@ -155,7 +177,7 @@ static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi) sspi->rx = rx; } - sspi->left_rx_cnt--; + sspi->left_rx_word--; } static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi) @@ -169,7 +191,7 @@ static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi) } writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); - sspi->left_tx_cnt--; + sspi->left_tx_word--; } static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi) @@ -184,7 +206,7 @@ static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi) sspi->rx = rx; } - sspi->left_rx_cnt--; + sspi->left_rx_word--; } static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi) @@ -198,7 +220,7 @@ static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi) } writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); - sspi->left_tx_cnt--; + sspi->left_tx_word--; } static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi) @@ -213,7 +235,7 @@ static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi) sspi->rx = rx; } - sspi->left_rx_cnt--; + sspi->left_rx_word--; } @@ -228,7 +250,7 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi) } writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); - sspi->left_tx_cnt--; + sspi->left_tx_word--; } static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) @@ -241,7 +263,7 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) /* Error Conditions */ if (spi_stat & SIRFSOC_SPI_RX_OFLOW || spi_stat & SIRFSOC_SPI_TX_UFLOW) { - complete(&sspi->done); + complete(&sspi->rx_done); writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); } @@ -249,50 +271,61 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) | SIRFSOC_SPI_RXFIFO_THD_REACH)) while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) & SIRFSOC_SPI_FIFO_EMPTY)) && - sspi->left_rx_cnt) + sspi->left_rx_word) sspi->rx_word(sspi); if (spi_stat & (SIRFSOC_SPI_FIFO_EMPTY | SIRFSOC_SPI_TXFIFO_THD_REACH)) while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) & SIRFSOC_SPI_FIFO_FULL)) && - sspi->left_tx_cnt) + sspi->left_tx_word) sspi->tx_word(sspi); /* Received all words */ - if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) { - complete(&sspi->done); + if ((sspi->left_rx_word == 0) && (sspi->left_tx_word == 0)) { + complete(&sspi->rx_done); writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); } return IRQ_HANDLED; } +static void spi_sirfsoc_dma_fini_callback(void *data) +{ + struct completion *dma_complete = data; + + complete(dma_complete); +} + static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) { struct sirfsoc_spi *sspi; int timeout = t->len * 10; sspi = spi_master_get_devdata(spi->master); - sspi->tx = t->tx_buf; - sspi->rx = t->rx_buf; - sspi->left_tx_cnt = sspi->left_rx_cnt = t->len; - INIT_COMPLETION(sspi->done); + sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage; + sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage; + sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width; + INIT_COMPLETION(sspi->rx_done); + INIT_COMPLETION(sspi->tx_done); writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); - if (t->len == 1) { + if (sspi->left_tx_word == 1) { writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_ENA_AUTO_CLR, sspi->base + SIRFSOC_SPI_CTRL); writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); - } else if ((t->len > 1) && (t->len < SIRFSOC_SPI_DAT_FRM_LEN_MAX)) { + } else if ((sspi->left_tx_word > 1) && (sspi->left_tx_word < + SIRFSOC_SPI_DAT_FRM_LEN_MAX)) { writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_MUL_DAT_MODE | SIRFSOC_SPI_ENA_AUTO_CLR, sspi->base + SIRFSOC_SPI_CTRL); - writel(t->len - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(t->len - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); + writel(sspi->left_tx_word - 1, + sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); + writel(sspi->left_tx_word - 1, + sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); } else { writel(readl(sspi->base + SIRFSOC_SPI_CTRL), sspi->base + SIRFSOC_SPI_CTRL); @@ -305,17 +338,64 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - /* Send the first word to trigger the whole tx/rx process */ - sspi->tx_word(sspi); + if (IS_DMA_VALID(t)) { + struct dma_async_tx_descriptor *rx_desc, *tx_desc; + + sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, DMA_FROM_DEVICE); + rx_desc = dmaengine_prep_slave_single(sspi->rx_chan, + sspi->dst_start, t->len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + rx_desc->callback = spi_sirfsoc_dma_fini_callback; + rx_desc->callback_param = &sspi->rx_done; + + sspi->src_start = dma_map_single(&spi->dev, (void *)sspi->tx, t->len, DMA_TO_DEVICE); + tx_desc = dmaengine_prep_slave_single(sspi->tx_chan, + sspi->src_start, t->len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx_desc->callback = spi_sirfsoc_dma_fini_callback; + tx_desc->callback_param = &sspi->tx_done; + + dmaengine_submit(tx_desc); + dmaengine_submit(rx_desc); + dma_async_issue_pending(sspi->tx_chan); + dma_async_issue_pending(sspi->rx_chan); + } else { + /* Send the first word to trigger the whole tx/rx process */ + sspi->tx_word(sspi); + + writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | + SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN | + SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN | + SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN); + } - writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | - SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN | - SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN | - SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN); writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN); - if (wait_for_completion_timeout(&sspi->done, timeout) == 0) + if (!IS_DMA_VALID(t)) { /* for PIO */ + if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) + dev_err(&spi->dev, "transfer timeout\n"); + } else if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) { dev_err(&spi->dev, "transfer timeout\n"); + dmaengine_terminate_all(sspi->rx_chan); + } else + sspi->left_rx_word = 0; + + /* + * we only wait tx-done event if transferring by DMA. for PIO, + * we get rx data by writing tx data, so if rx is done, tx has + * done earlier + */ + if (IS_DMA_VALID(t)) { + if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { + dev_err(&spi->dev, "transfer timeout\n"); + dmaengine_terminate_all(sspi->tx_chan); + } + } + + if (IS_DMA_VALID(t)) { + dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE); + dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE); + } /* TX, RX FIFO stop */ writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); @@ -323,7 +403,7 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN); writel(0, sspi->base + SIRFSOC_SPI_INT_EN); - return t->len - sspi->left_rx_cnt; + return t->len - sspi->left_rx_word * sspi->word_width; } static void spi_sirfsoc_chipselect(struct spi_device *spi, int value) @@ -332,7 +412,6 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value) if (sspi->chipselect[spi->chip_select] == 0) { u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL); - regval |= SIRFSOC_SPI_CS_IO_OUT; switch (value) { case BITBANG_CS_ACTIVE: if (spi->mode & SPI_CS_HIGH) @@ -369,11 +448,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz; - /* Enable IO mode for RX, TX */ - writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); - writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); regval = (sspi->ctrl_freq / (2 * hz)) - 1; - if (regval > 0xFFFF || regval < 0) { dev_err(&spi->dev, "Speed %d not supported\n", hz); return -EINVAL; @@ -388,6 +463,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) SIRFSOC_SPI_FIFO_WIDTH_BYTE; rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | SIRFSOC_SPI_FIFO_WIDTH_BYTE; + sspi->word_width = 1; break; case 12: case 16: @@ -399,6 +475,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) SIRFSOC_SPI_FIFO_WIDTH_WORD; rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | SIRFSOC_SPI_FIFO_WIDTH_WORD; + sspi->word_width = 2; break; case 32: regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32; @@ -408,6 +485,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) SIRFSOC_SPI_FIFO_WIDTH_DWORD; rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | SIRFSOC_SPI_FIFO_WIDTH_DWORD; + sspi->word_width = 4; break; default: BUG(); @@ -442,6 +520,17 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL); writel(regval, sspi->base + SIRFSOC_SPI_CTRL); + + if (IS_DMA_VALID(t)) { + /* Enable DMA mode for RX, TX */ + writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); + writel(SIRFSOC_SPI_RX_DMA_FLUSH, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + } else { + /* Enable IO mode for RX, TX */ + writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); + writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + } + return 0; } @@ -466,6 +555,8 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) struct spi_master *master; struct resource *mem_res; int num_cs, cs_gpio, irq; + u32 rx_dma_ch, tx_dma_ch; + dma_cap_mask_t dma_cap_mask; int i; int ret; @@ -476,6 +567,20 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) goto err_cs; } + ret = of_property_read_u32(pdev->dev.of_node, + "sirf,spi-dma-rx-channel", &rx_dma_ch); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to get rx dma channel\n"); + goto err_cs; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "sirf,spi-dma-tx-channel", &tx_dma_ch); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to get tx dma channel\n"); + goto err_cs; + } + master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI master\n"); @@ -484,12 +589,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Unable to get IO resource\n"); - ret = -ENODEV; - goto free_master; - } master->num_chipselect = num_cs; for (i = 0; i < master->num_chipselect; i++) { @@ -516,6 +615,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) } } + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sspi->base = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sspi->base)) { ret = PTR_ERR(sspi->base); @@ -538,19 +638,40 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; sspi->bitbang.master->setup = spi_sirfsoc_setup; master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) | SPI_BPW_MASK(16) | SPI_BPW_MASK(32); sspi->bitbang.master->dev.of_node = pdev->dev.of_node; + /* request DMA channels */ + dma_cap_zero(dma_cap_mask); + dma_cap_set(DMA_INTERLEAVE, dma_cap_mask); + + sspi->rx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id, + (void *)rx_dma_ch); + if (!sspi->rx_chan) { + dev_err(&pdev->dev, "can not allocate rx dma channel\n"); + ret = -ENODEV; + goto free_master; + } + sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id, + (void *)tx_dma_ch); + if (!sspi->tx_chan) { + dev_err(&pdev->dev, "can not allocate tx dma channel\n"); + ret = -ENODEV; + goto free_rx_dma; + } + sspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(sspi->clk)) { - ret = -EINVAL; - goto free_master; + ret = PTR_ERR(sspi->clk); + goto free_tx_dma; } clk_prepare_enable(sspi->clk); sspi->ctrl_freq = clk_get_rate(sspi->clk); - init_completion(&sspi->done); + init_completion(&sspi->rx_done); + init_completion(&sspi->tx_done); writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); @@ -559,17 +680,28 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) /* We are not using dummy delay between command and data */ writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL); + sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL); + if (!sspi->dummypage) { + ret = -ENOMEM; + goto free_clk; + } + ret = spi_bitbang_start(&sspi->bitbang); if (ret) - goto free_clk; + goto free_dummypage; dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num); return 0; - +free_dummypage: + kfree(sspi->dummypage); free_clk: clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); +free_tx_dma: + dma_release_channel(sspi->tx_chan); +free_rx_dma: + dma_release_channel(sspi->rx_chan); free_master: spi_master_put(master); err_cs: @@ -590,8 +722,11 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) if (sspi->chipselect[i] > 0) gpio_free(sspi->chipselect[i]); } + kfree(sspi->dummypage); clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); + dma_release_channel(sspi->rx_chan); + dma_release_channel(sspi->tx_chan); spi_master_put(master); return 0; } @@ -599,8 +734,7 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int spi_sirfsoc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct sirfsoc_spi *sspi = spi_master_get_devdata(master); clk_disable(sspi->clk); @@ -609,8 +743,7 @@ static int spi_sirfsoc_suspend(struct device *dev) static int spi_sirfsoc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct sirfsoc_spi *sspi = spi_master_get_devdata(master); clk_enable(sspi->clk); diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index e8f542a..145dd43 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -816,14 +816,6 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, msg->status = 0; msg->actual_length = 0; - ret = pm_runtime_get_sync(tspi->dev); - if (ret < 0) { - dev_err(tspi->dev, "runtime PM get failed: %d\n", ret); - msg->status = ret; - spi_finalize_current_message(master); - return ret; - } - single_xfer = list_is_singular(&msg->transfers); list_for_each_entry(xfer, &msg->transfers, transfer_list) { INIT_COMPLETION(tspi->xfer_completion); @@ -859,7 +851,6 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, ret = 0; exit: tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); - pm_runtime_put(tspi->dev); msg->status = ret; spi_finalize_current_message(master); return ret; @@ -1053,24 +1044,19 @@ static int tegra_spi_probe(struct platform_device *pdev) master->transfer_one_message = tegra_spi_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; + master->auto_runtime_pm = true; tspi->master = master; tspi->dev = &pdev->dev; spin_lock_init(&tspi->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "No IO memory resource\n"); - ret = -ENODEV; - goto exit_free_master; - } - tspi->phys = r->start; tspi->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(tspi->base)) { ret = PTR_ERR(tspi->base); - dev_err(&pdev->dev, "ioremap failed: err = %d\n", ret); goto exit_free_master; } + tspi->phys = r->start; spi_irq = platform_get_irq(pdev, 0); tspi->irq = spi_irq; diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index c1d5d95..1d814dc 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -335,12 +335,6 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master, struct spi_device *spi = msg->spi; int ret; - ret = pm_runtime_get_sync(tsd->dev); - if (ret < 0) { - dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret); - return ret; - } - msg->status = 0; msg->actual_length = 0; single_xfer = list_is_singular(&msg->transfers); @@ -380,7 +374,6 @@ exit: tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND); msg->status = ret; spi_finalize_current_message(master); - pm_runtime_put(tsd->dev); return ret; } @@ -477,6 +470,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; master->setup = tegra_sflash_setup; master->transfer_one_message = tegra_sflash_transfer_one_message; + master->auto_runtime_pm = true; master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 80490cc..c703536 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -836,11 +836,6 @@ static int tegra_slink_transfer_one_message(struct spi_master *master, msg->status = 0; msg->actual_length = 0; - ret = pm_runtime_get_sync(tspi->dev); - if (ret < 0) { - dev_err(tspi->dev, "runtime get failed: %d\n", ret); - goto done; - } single_xfer = list_is_singular(&msg->transfers); list_for_each_entry(xfer, &msg->transfers, transfer_list) { @@ -878,8 +873,6 @@ static int tegra_slink_transfer_one_message(struct spi_master *master, exit: tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); - pm_runtime_put(tspi->dev); -done: msg->status = ret; spi_finalize_current_message(master); return ret; @@ -1086,6 +1079,7 @@ static int tegra_slink_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->setup = tegra_slink_setup; master->transfer_one_message = tegra_slink_transfer_one_message; + master->auto_runtime_pm = true; master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c new file mode 100644 index 0000000..e12d962 --- /dev/null +++ b/drivers/spi/spi-ti-qspi.c @@ -0,0 +1,574 @@ +/* + * TI QSPI driver + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * Author: Sourav Poddar <sourav.poddar@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GPLv2. + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/omap-dma.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include <linux/spi/spi.h> + +struct ti_qspi_regs { + u32 clkctrl; +}; + +struct ti_qspi { + struct completion transfer_complete; + + /* IRQ synchronization */ + spinlock_t lock; + + /* list synchronization */ + struct mutex list_lock; + + struct spi_master *master; + void __iomem *base; + struct clk *fclk; + struct device *dev; + + struct ti_qspi_regs ctx_reg; + + u32 spi_max_frequency; + u32 cmd; + u32 dc; + u32 stat; +}; + +#define QSPI_PID (0x0) +#define QSPI_SYSCONFIG (0x10) +#define QSPI_INTR_STATUS_RAW_SET (0x20) +#define QSPI_INTR_STATUS_ENABLED_CLEAR (0x24) +#define QSPI_INTR_ENABLE_SET_REG (0x28) +#define QSPI_INTR_ENABLE_CLEAR_REG (0x2c) +#define QSPI_SPI_CLOCK_CNTRL_REG (0x40) +#define QSPI_SPI_DC_REG (0x44) +#define QSPI_SPI_CMD_REG (0x48) +#define QSPI_SPI_STATUS_REG (0x4c) +#define QSPI_SPI_DATA_REG (0x50) +#define QSPI_SPI_SETUP0_REG (0x54) +#define QSPI_SPI_SWITCH_REG (0x64) +#define QSPI_SPI_SETUP1_REG (0x58) +#define QSPI_SPI_SETUP2_REG (0x5c) +#define QSPI_SPI_SETUP3_REG (0x60) +#define QSPI_SPI_DATA_REG_1 (0x68) +#define QSPI_SPI_DATA_REG_2 (0x6c) +#define QSPI_SPI_DATA_REG_3 (0x70) + +#define QSPI_COMPLETION_TIMEOUT msecs_to_jiffies(2000) + +#define QSPI_FCLK 192000000 + +/* Clock Control */ +#define QSPI_CLK_EN (1 << 31) +#define QSPI_CLK_DIV_MAX 0xffff + +/* Command */ +#define QSPI_EN_CS(n) (n << 28) +#define QSPI_WLEN(n) ((n - 1) << 19) +#define QSPI_3_PIN (1 << 18) +#define QSPI_RD_SNGL (1 << 16) +#define QSPI_WR_SNGL (2 << 16) +#define QSPI_RD_DUAL (3 << 16) +#define QSPI_RD_QUAD (7 << 16) +#define QSPI_INVAL (4 << 16) +#define QSPI_WC_CMD_INT_EN (1 << 14) +#define QSPI_FLEN(n) ((n - 1) << 0) + +/* STATUS REGISTER */ +#define WC 0x02 + +/* INTERRUPT REGISTER */ +#define QSPI_WC_INT_EN (1 << 1) +#define QSPI_WC_INT_DISABLE (1 << 1) + +/* Device Control */ +#define QSPI_DD(m, n) (m << (3 + n * 8)) +#define QSPI_CKPHA(n) (1 << (2 + n * 8)) +#define QSPI_CSPOL(n) (1 << (1 + n * 8)) +#define QSPI_CKPOL(n) (1 << (n * 8)) + +#define QSPI_FRAME 4096 + +#define QSPI_AUTOSUSPEND_TIMEOUT 2000 + +static inline unsigned long ti_qspi_read(struct ti_qspi *qspi, + unsigned long reg) +{ + return readl(qspi->base + reg); +} + +static inline void ti_qspi_write(struct ti_qspi *qspi, + unsigned long val, unsigned long reg) +{ + writel(val, qspi->base + reg); +} + +static int ti_qspi_setup(struct spi_device *spi) +{ + struct ti_qspi *qspi = spi_master_get_devdata(spi->master); + struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg; + int clk_div = 0, ret; + u32 clk_ctrl_reg, clk_rate, clk_mask; + + if (spi->master->busy) { + dev_dbg(qspi->dev, "master busy doing other trasnfers\n"); + return -EBUSY; + } + + if (!qspi->spi_max_frequency) { + dev_err(qspi->dev, "spi max frequency not defined\n"); + return -EINVAL; + } + + clk_rate = clk_get_rate(qspi->fclk); + + clk_div = DIV_ROUND_UP(clk_rate, qspi->spi_max_frequency) - 1; + + if (clk_div < 0) { + dev_dbg(qspi->dev, "clock divider < 0, using /1 divider\n"); + return -EINVAL; + } + + if (clk_div > QSPI_CLK_DIV_MAX) { + dev_dbg(qspi->dev, "clock divider >%d , using /%d divider\n", + QSPI_CLK_DIV_MAX, QSPI_CLK_DIV_MAX + 1); + return -EINVAL; + } + + dev_dbg(qspi->dev, "hz: %d, clock divider %d\n", + qspi->spi_max_frequency, clk_div); + + ret = pm_runtime_get_sync(qspi->dev); + if (ret) { + dev_err(qspi->dev, "pm_runtime_get_sync() failed\n"); + return ret; + } + + clk_ctrl_reg = ti_qspi_read(qspi, QSPI_SPI_CLOCK_CNTRL_REG); + + clk_ctrl_reg &= ~QSPI_CLK_EN; + + /* disable SCLK */ + ti_qspi_write(qspi, clk_ctrl_reg, QSPI_SPI_CLOCK_CNTRL_REG); + + /* enable SCLK */ + clk_mask = QSPI_CLK_EN | clk_div; + ti_qspi_write(qspi, clk_mask, QSPI_SPI_CLOCK_CNTRL_REG); + ctx_reg->clkctrl = clk_mask; + + pm_runtime_mark_last_busy(qspi->dev); + ret = pm_runtime_put_autosuspend(qspi->dev); + if (ret < 0) { + dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n"); + return ret; + } + + return 0; +} + +static void ti_qspi_restore_ctx(struct ti_qspi *qspi) +{ + struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg; + + ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG); +} + +static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) +{ + int wlen, count, ret; + unsigned int cmd; + const u8 *txbuf; + + txbuf = t->tx_buf; + cmd = qspi->cmd | QSPI_WR_SNGL; + count = t->len; + wlen = t->bits_per_word; + + while (count) { + switch (wlen) { + case 8: + dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n", + cmd, qspi->dc, *txbuf); + writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG); + ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); + ret = wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT); + if (ret == 0) { + dev_err(qspi->dev, "write timed out\n"); + return -ETIMEDOUT; + } + txbuf += 1; + count -= 1; + break; + case 16: + dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n", + cmd, qspi->dc, *txbuf); + writew(*((u16 *)txbuf), qspi->base + QSPI_SPI_DATA_REG); + ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); + ret = wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT); + if (ret == 0) { + dev_err(qspi->dev, "write timed out\n"); + return -ETIMEDOUT; + } + txbuf += 2; + count -= 2; + break; + case 32: + dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %08x\n", + cmd, qspi->dc, *txbuf); + writel(*((u32 *)txbuf), qspi->base + QSPI_SPI_DATA_REG); + ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); + ret = wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT); + if (ret == 0) { + dev_err(qspi->dev, "write timed out\n"); + return -ETIMEDOUT; + } + txbuf += 4; + count -= 4; + break; + } + } + + return 0; +} + +static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) +{ + int wlen, count, ret; + unsigned int cmd; + u8 *rxbuf; + + rxbuf = t->rx_buf; + cmd = qspi->cmd; + switch (t->rx_nbits) { + case SPI_NBITS_DUAL: + cmd |= QSPI_RD_DUAL; + break; + case SPI_NBITS_QUAD: + cmd |= QSPI_RD_QUAD; + break; + default: + cmd |= QSPI_RD_SNGL; + break; + } + count = t->len; + wlen = t->bits_per_word; + + while (count) { + dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc); + ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); + ret = wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT); + if (ret == 0) { + dev_err(qspi->dev, "read timed out\n"); + return -ETIMEDOUT; + } + switch (wlen) { + case 8: + *rxbuf = readb(qspi->base + QSPI_SPI_DATA_REG); + rxbuf += 1; + count -= 1; + break; + case 16: + *((u16 *)rxbuf) = readw(qspi->base + QSPI_SPI_DATA_REG); + rxbuf += 2; + count -= 2; + break; + case 32: + *((u32 *)rxbuf) = readl(qspi->base + QSPI_SPI_DATA_REG); + rxbuf += 4; + count -= 4; + break; + } + } + + return 0; +} + +static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t) +{ + int ret; + + if (t->tx_buf) { + ret = qspi_write_msg(qspi, t); + if (ret) { + dev_dbg(qspi->dev, "Error while writing\n"); + return ret; + } + } + + if (t->rx_buf) { + ret = qspi_read_msg(qspi, t); + if (ret) { + dev_dbg(qspi->dev, "Error while reading\n"); + return ret; + } + } + + return 0; +} + +static int ti_qspi_start_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct ti_qspi *qspi = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t; + int status = 0, ret; + int frame_length; + + /* setup device control reg */ + qspi->dc = 0; + + if (spi->mode & SPI_CPHA) + qspi->dc |= QSPI_CKPHA(spi->chip_select); + if (spi->mode & SPI_CPOL) + qspi->dc |= QSPI_CKPOL(spi->chip_select); + if (spi->mode & SPI_CS_HIGH) + qspi->dc |= QSPI_CSPOL(spi->chip_select); + + frame_length = (m->frame_length << 3) / spi->bits_per_word; + + frame_length = clamp(frame_length, 0, QSPI_FRAME); + + /* setup command reg */ + qspi->cmd = 0; + qspi->cmd |= QSPI_EN_CS(spi->chip_select); + qspi->cmd |= QSPI_FLEN(frame_length); + qspi->cmd |= QSPI_WC_CMD_INT_EN; + + ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG); + ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG); + + mutex_lock(&qspi->list_lock); + + list_for_each_entry(t, &m->transfers, transfer_list) { + qspi->cmd |= QSPI_WLEN(t->bits_per_word); + + ret = qspi_transfer_msg(qspi, t); + if (ret) { + dev_dbg(qspi->dev, "transfer message failed\n"); + mutex_unlock(&qspi->list_lock); + return -EINVAL; + } + + m->actual_length += t->len; + } + + mutex_unlock(&qspi->list_lock); + + m->status = status; + spi_finalize_current_message(master); + + ti_qspi_write(qspi, qspi->cmd | QSPI_INVAL, QSPI_SPI_CMD_REG); + + return status; +} + +static irqreturn_t ti_qspi_isr(int irq, void *dev_id) +{ + struct ti_qspi *qspi = dev_id; + u16 int_stat; + + irqreturn_t ret = IRQ_HANDLED; + + spin_lock(&qspi->lock); + + int_stat = ti_qspi_read(qspi, QSPI_INTR_STATUS_ENABLED_CLEAR); + qspi->stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG); + + if (!int_stat) { + dev_dbg(qspi->dev, "No IRQ triggered\n"); + ret = IRQ_NONE; + goto out; + } + + ret = IRQ_WAKE_THREAD; + + ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_ENABLE_CLEAR_REG); + ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, + QSPI_INTR_STATUS_ENABLED_CLEAR); + +out: + spin_unlock(&qspi->lock); + + return ret; +} + +static irqreturn_t ti_qspi_threaded_isr(int this_irq, void *dev_id) +{ + struct ti_qspi *qspi = dev_id; + unsigned long flags; + + spin_lock_irqsave(&qspi->lock, flags); + + if (qspi->stat & WC) + complete(&qspi->transfer_complete); + + spin_unlock_irqrestore(&qspi->lock, flags); + + ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG); + + return IRQ_HANDLED; +} + +static int ti_qspi_runtime_resume(struct device *dev) +{ + struct ti_qspi *qspi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + qspi = spi_master_get_devdata(master); + ti_qspi_restore_ctx(qspi); + + return 0; +} + +static const struct of_device_id ti_qspi_match[] = { + {.compatible = "ti,dra7xxx-qspi" }, + {.compatible = "ti,am4372-qspi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ti_qspi_match); + +static int ti_qspi_probe(struct platform_device *pdev) +{ + struct ti_qspi *qspi; + struct spi_master *master; + struct resource *r; + struct device_node *np = pdev->dev.of_node; + u32 max_freq; + int ret = 0, num_cs, irq; + + master = spi_alloc_master(&pdev->dev, sizeof(*qspi)); + if (!master) + return -ENOMEM; + + master->mode_bits = SPI_CPOL | SPI_CPHA; + + master->bus_num = -1; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = ti_qspi_setup; + master->auto_runtime_pm = true; + master->transfer_one_message = ti_qspi_start_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + + if (!of_property_read_u32(np, "num-cs", &num_cs)) + master->num_chipselect = num_cs; + + platform_set_drvdata(pdev, master); + + qspi = spi_master_get_devdata(master); + qspi->master = master; + qspi->dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return irq; + } + + spin_lock_init(&qspi->lock); + mutex_init(&qspi->list_lock); + + qspi->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(qspi->base)) { + ret = PTR_ERR(qspi->base); + goto free_master; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, ti_qspi_isr, + ti_qspi_threaded_isr, 0, + dev_name(&pdev->dev), qspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + irq); + goto free_master; + } + + qspi->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(qspi->fclk)) { + ret = PTR_ERR(qspi->fclk); + dev_err(&pdev->dev, "could not get clk: %d\n", ret); + } + + init_completion(&qspi->transfer_complete); + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, QSPI_AUTOSUSPEND_TIMEOUT); + pm_runtime_enable(&pdev->dev); + + if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) + qspi->spi_max_frequency = max_freq; + + ret = spi_register_master(master); + if (ret) + goto free_master; + + return 0; + +free_master: + spi_master_put(master); + return ret; +} + +static int ti_qspi_remove(struct platform_device *pdev) +{ + struct ti_qspi *qspi = platform_get_drvdata(pdev); + + spi_unregister_master(qspi->master); + + return 0; +} + +static const struct dev_pm_ops ti_qspi_pm_ops = { + .runtime_resume = ti_qspi_runtime_resume, +}; + +static struct platform_driver ti_qspi_driver = { + .probe = ti_qspi_probe, + .remove = ti_qspi_remove, + .driver = { + .name = "ti,dra7xxx-qspi", + .owner = THIS_MODULE, + .pm = &ti_qspi_pm_ops, + .of_match_table = ti_qspi_match, + } +}; + +module_platform_driver(ti_qspi_driver); + +MODULE_AUTHOR("Sourav Poddar <sourav.poddar@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI QSPI controller driver"); diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c index 10606fc..7d20e12 100644 --- a/drivers/spi/spi-ti-ssp.c +++ b/drivers/spi/spi-ti-ssp.c @@ -283,7 +283,7 @@ static int ti_ssp_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int error = 0; - pdata = dev->platform_data; + pdata = dev_get_platdata(dev); if (!pdata) { dev_err(dev, "platform data not found\n"); return -EINVAL; diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 6b0874d..2d4010d 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -52,8 +52,7 @@ static inline int tle62x0_write(struct tle62x0_state *st) buff[1] = gpio_state; } - dev_dbg(&st->us->dev, "buff %02x,%02x,%02x\n", - buff[0], buff[1], buff[2]); + dev_dbg(&st->us->dev, "buff %3ph\n", buff); return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2); } @@ -247,7 +246,7 @@ static int tle62x0_probe(struct spi_device *spi) int ptr; int ret; - pdata = spi->dev.platform_data; + pdata = dev_get_platdata(&spi->dev); if (pdata == NULL) { dev_err(&spi->dev, "no device data specified\n"); return -EINVAL; diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index dd55707..eaeeed5 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1797,3 +1797,5 @@ MODULE_PARM_DESC(use_dma, MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor ML7xxx IOH SPI Driver"); +MODULE_DEVICE_TABLE(pci, pch_spi_pcidev_id); + diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index e9b7681..7c6d157 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -26,7 +26,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> -#include <asm/gpio.h> +#include <linux/gpio.h> #define SPI_FIFO_SIZE 4 diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 09a9428..0bf1b2c 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -80,10 +80,9 @@ struct xilinx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; struct completion done; - struct resource mem; /* phys mem */ void __iomem *regs; /* virt. address of the control registers */ - u32 irq; + int irq; u8 *rx_ptr; /* pointer in the Tx buffer */ const u8 *tx_ptr; /* pointer in the Rx buffer */ @@ -233,21 +232,6 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, return 0; } -static int xilinx_spi_setup(struct spi_device *spi) -{ - /* always return 0, we can not check the number of bits. - * There are cases when SPI setup is called before any driver is - * there, in that case the SPI core defaults to 8 bits, which we - * do not support in some cases. But if we return an error, the - * SPI device would not be registered and no driver can get hold of it - * When the driver is there, it will call SPI setup again with the - * correct number of bits per transfer. - * If a driver setups with the wrong bit number, it will fail when - * it tries to do a transfer - */ - return 0; -} - static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) { u8 sr; @@ -355,17 +339,34 @@ static const struct of_device_id xilinx_spi_of_match[] = { }; MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); -struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, - u32 irq, s16 bus_num, int num_cs, int bits_per_word) +static int xilinx_spi_probe(struct platform_device *pdev) { - struct spi_master *master; struct xilinx_spi *xspi; - int ret; + struct xspi_platform_data *pdata; + struct resource *res; + int ret, num_cs = 0, bits_per_word = 8; + struct spi_master *master; u32 tmp; + u8 i; + + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + num_cs = pdata->num_chipselect; + bits_per_word = pdata->bits_per_word; + } else { + of_property_read_u32(pdev->dev.of_node, "xlnx,num-ss-bits", + &num_cs); + } + + if (!num_cs) { + dev_err(&pdev->dev, + "Missing slave select configuration data\n"); + return -EINVAL; + } - master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi)); if (!master) - return NULL; + return -ENODEV; /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA; @@ -375,25 +376,18 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; - xspi->bitbang.master->setup = xilinx_spi_setup; init_completion(&xspi->done); - if (!request_mem_region(mem->start, resource_size(mem), - XILINX_SPI_NAME)) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xspi->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xspi->regs)) { + ret = PTR_ERR(xspi->regs); goto put_master; - - xspi->regs = ioremap(mem->start, resource_size(mem)); - if (xspi->regs == NULL) { - dev_warn(dev, "ioremap failure\n"); - goto map_failed; } - master->bus_num = bus_num; + master->bus_num = pdev->dev.id; master->num_chipselect = num_cs; - master->dev.of_node = dev->of_node; - - xspi->mem = *mem; - xspi->irq = irq; + master->dev.of_node = pdev->dev.of_node; /* * Detect endianess on the IP via loop bit in CR. Detection @@ -423,113 +417,63 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, } else if (xspi->bits_per_word == 32) { xspi->tx_fn = xspi_tx32; xspi->rx_fn = xspi_rx32; - } else - goto unmap_io; - + } else { + ret = -EINVAL; + goto put_master; + } /* SPI controller initializations */ xspi_init_hw(xspi); + xspi->irq = platform_get_irq(pdev, 0); + if (xspi->irq < 0) { + ret = xspi->irq; + goto put_master; + } + /* Register for SPI Interrupt */ - ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); + ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, + dev_name(&pdev->dev), xspi); if (ret) - goto unmap_io; + goto put_master; ret = spi_bitbang_start(&xspi->bitbang); if (ret) { - dev_err(dev, "spi_bitbang_start FAILED\n"); - goto free_irq; - } - - dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", - (unsigned long long)mem->start, xspi->regs, xspi->irq); - return master; - -free_irq: - free_irq(xspi->irq, xspi); -unmap_io: - iounmap(xspi->regs); -map_failed: - release_mem_region(mem->start, resource_size(mem)); -put_master: - spi_master_put(master); - return NULL; -} -EXPORT_SYMBOL(xilinx_spi_init); - -void xilinx_spi_deinit(struct spi_master *master) -{ - struct xilinx_spi *xspi; - - xspi = spi_master_get_devdata(master); - - spi_bitbang_stop(&xspi->bitbang); - free_irq(xspi->irq, xspi); - iounmap(xspi->regs); - - release_mem_region(xspi->mem.start, resource_size(&xspi->mem)); - spi_master_put(xspi->bitbang.master); -} -EXPORT_SYMBOL(xilinx_spi_deinit); - -static int xilinx_spi_probe(struct platform_device *dev) -{ - struct xspi_platform_data *pdata; - struct resource *r; - int irq, num_cs = 0, bits_per_word = 8; - struct spi_master *master; - u8 i; - - pdata = dev->dev.platform_data; - if (pdata) { - num_cs = pdata->num_chipselect; - bits_per_word = pdata->bits_per_word; - } - -#ifdef CONFIG_OF - if (dev->dev.of_node) { - const __be32 *prop; - int len; - - /* number of slave select bits is required */ - prop = of_get_property(dev->dev.of_node, "xlnx,num-ss-bits", - &len); - if (prop && len >= sizeof(*prop)) - num_cs = __be32_to_cpup(prop); - } -#endif - - if (!num_cs) { - dev_err(&dev->dev, "Missing slave select configuration data\n"); - return -EINVAL; + dev_err(&pdev->dev, "spi_bitbang_start FAILED\n"); + goto put_master; } - - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!r) - return -ENODEV; - - irq = platform_get_irq(dev, 0); - if (irq < 0) - return -ENXIO; - - master = xilinx_spi_init(&dev->dev, r, irq, dev->id, num_cs, - bits_per_word); - if (!master) - return -ENODEV; + dev_info(&pdev->dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", + (unsigned long long)res->start, xspi->regs, xspi->irq); if (pdata) { for (i = 0; i < pdata->num_devices; i++) spi_new_device(master, pdata->devices + i); } - platform_set_drvdata(dev, master); + platform_set_drvdata(pdev, master); return 0; + +put_master: + spi_master_put(master); + + return ret; } -static int xilinx_spi_remove(struct platform_device *dev) +static int xilinx_spi_remove(struct platform_device *pdev) { - xilinx_spi_deinit(platform_get_drvdata(dev)); + struct spi_master *master = platform_get_drvdata(pdev); + struct xilinx_spi *xspi = spi_master_get_devdata(master); + void __iomem *regs_base = xspi->regs; + + spi_bitbang_stop(&xspi->bitbang); + + /* Disable all the interrupts just in case */ + xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); + /* Disable the global IPIF interrupt */ + xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); + + spi_master_put(xspi->bitbang.master); return 0; } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2..9e039c6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -553,6 +553,10 @@ static void spi_pump_messages(struct kthread_work *work) master->unprepare_transfer_hardware(master)) dev_err(&master->dev, "failed to unprepare transfer hardware\n"); + if (master->auto_runtime_pm) { + pm_runtime_mark_last_busy(master->dev.parent); + pm_runtime_put_autosuspend(master->dev.parent); + } return; } @@ -572,11 +576,23 @@ static void spi_pump_messages(struct kthread_work *work) master->busy = true; spin_unlock_irqrestore(&master->queue_lock, flags); + if (!was_busy && master->auto_runtime_pm) { + ret = pm_runtime_get_sync(master->dev.parent); + if (ret < 0) { + dev_err(&master->dev, "Failed to power device: %d\n", + ret); + return; + } + } + if (!was_busy && master->prepare_transfer_hardware) { ret = master->prepare_transfer_hardware(master); if (ret) { dev_err(&master->dev, "failed to prepare transfer hardware\n"); + + if (master->auto_runtime_pm) + pm_runtime_put(master->dev.parent); return; } } @@ -774,7 +790,7 @@ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) msg->status = -EINPROGRESS; list_add_tail(&msg->queue, &master->queue); - if (master->running && !master->busy) + if (!master->busy) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); @@ -869,6 +885,47 @@ static void of_register_spi_devices(struct spi_master *master) if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; + /* Device DUAL/QUAD mode */ + prop = of_get_property(nc, "spi-tx-bus-width", &len); + if (prop && len == sizeof(*prop)) { + switch (be32_to_cpup(prop)) { + case SPI_NBITS_SINGLE: + break; + case SPI_NBITS_DUAL: + spi->mode |= SPI_TX_DUAL; + break; + case SPI_NBITS_QUAD: + spi->mode |= SPI_TX_QUAD; + break; + default: + dev_err(&master->dev, + "spi-tx-bus-width %d not supported\n", + be32_to_cpup(prop)); + spi_dev_put(spi); + continue; + } + } + + prop = of_get_property(nc, "spi-rx-bus-width", &len); + if (prop && len == sizeof(*prop)) { + switch (be32_to_cpup(prop)) { + case SPI_NBITS_SINGLE: + break; + case SPI_NBITS_DUAL: + spi->mode |= SPI_RX_DUAL; + break; + case SPI_NBITS_QUAD: + spi->mode |= SPI_RX_QUAD; + break; + default: + dev_err(&master->dev, + "spi-rx-bus-width %d not supported\n", + be32_to_cpup(prop)); + spi_dev_put(spi); + continue; + } + } + /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); if (!prop || len < sizeof(*prop)) { @@ -1169,7 +1226,7 @@ int spi_register_master(struct spi_master *master) else { status = spi_master_initialize_queue(master); if (status) { - device_unregister(&master->dev); + device_del(&master->dev); goto done; } } @@ -1316,6 +1373,19 @@ int spi_setup(struct spi_device *spi) unsigned bad_bits; int status = 0; + /* check mode to prevent that DUAL and QUAD set at the same time + */ + if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) || + ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) { + dev_err(&spi->dev, + "setup: can not select dual and quad at the same time\n"); + return -EINVAL; + } + /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden + */ + if ((spi->mode & SPI_3WIRE) && (spi->mode & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) + return -EINVAL; /* help drivers fail *cleanly* when they need options * that aren't supported with their current master */ @@ -1351,6 +1421,11 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) struct spi_master *master = spi->master; struct spi_transfer *xfer; + if (list_empty(&message->transfers)) + return -EINVAL; + if (!message->complete) + return -EINVAL; + /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by @@ -1373,12 +1448,20 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) /** * Set transfer bits_per_word and max speed as spi device default if * it is not set for this transfer. + * Set transfer tx_nbits and rx_nbits as single transfer default + * (SPI_NBITS_SINGLE) if it is not set for this transfer. */ list_for_each_entry(xfer, &message->transfers, transfer_list) { + message->frame_length += xfer->len; if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; - if (!xfer->speed_hz) + if (!xfer->speed_hz) { xfer->speed_hz = spi->max_speed_hz; + if (master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + xfer->speed_hz = master->max_speed_hz; + } + if (master->bits_per_word_mask) { /* Only 32 bits fit in the mask */ if (xfer->bits_per_word > 32) @@ -1387,6 +1470,54 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) BIT(xfer->bits_per_word - 1))) return -EINVAL; } + + if (xfer->speed_hz && master->min_speed_hz && + xfer->speed_hz < master->min_speed_hz) + return -EINVAL; + if (xfer->speed_hz && master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + return -EINVAL; + + if (xfer->tx_buf && !xfer->tx_nbits) + xfer->tx_nbits = SPI_NBITS_SINGLE; + if (xfer->rx_buf && !xfer->rx_nbits) + xfer->rx_nbits = SPI_NBITS_SINGLE; + /* check transfer tx/rx_nbits: + * 1. keep the value is not out of single, dual and quad + * 2. keep tx/rx_nbits is contained by mode in spi_device + * 3. if SPI_3WIRE, tx/rx_nbits should be in single + */ + if (xfer->tx_buf) { + if (xfer->tx_nbits != SPI_NBITS_SINGLE && + xfer->tx_nbits != SPI_NBITS_DUAL && + xfer->tx_nbits != SPI_NBITS_QUAD) + return -EINVAL; + if ((xfer->tx_nbits == SPI_NBITS_DUAL) && + !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD))) + return -EINVAL; + if ((xfer->tx_nbits == SPI_NBITS_QUAD) && + !(spi->mode & SPI_TX_QUAD)) + return -EINVAL; + if ((spi->mode & SPI_3WIRE) && + (xfer->tx_nbits != SPI_NBITS_SINGLE)) + return -EINVAL; + } + /* check transfer rx_nbits */ + if (xfer->rx_buf) { + if (xfer->rx_nbits != SPI_NBITS_SINGLE && + xfer->rx_nbits != SPI_NBITS_DUAL && + xfer->rx_nbits != SPI_NBITS_QUAD) + return -EINVAL; + if ((xfer->rx_nbits == SPI_NBITS_DUAL) && + !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD))) + return -EINVAL; + if ((xfer->rx_nbits == SPI_NBITS_QUAD) && + !(spi->mode & SPI_RX_QUAD)) + return -EINVAL; + if ((spi->mode & SPI_3WIRE) && + (xfer->rx_nbits != SPI_NBITS_SINGLE)) + return -EINVAL; + } } message->spi = spi; diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index e25eba5..b3b5125 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -482,7 +482,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) ret = comedi_device_postconfig(dev); if (ret < 0) { comedi_device_detach(dev); - module_put(dev->driver->module); + module_put(driv->module); } /* On success, the driver module count has been incremented. */ return ret; diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 3396eb9..ac27671 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -341,8 +341,8 @@ void hvsilib_establish(struct hvsi_priv *pv) pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); - /* Try for up to 200s */ - for (timeout = 0; timeout < 20; timeout++) { + /* Try for up to 400ms */ + for (timeout = 0; timeout < 40; timeout++) { if (pv->established) goto established; if (!hvsi_get_packet(pv)) diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 08613e2..279b049 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -304,6 +304,13 @@ static int __init ohci_pci_init(void) pr_info("%s: " DRIVER_DESC "\n", hcd_name); ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides); + +#ifdef CONFIG_PM + /* Entries for the PCI suspend/resume callbacks are special */ + ohci_pci_hc_driver.pci_suspend = ohci_suspend; + ohci_pci_hc_driver.pci_resume = ohci_resume; +#endif + return pci_register_driver(&ohci_pci_driver); } module_init(ohci_pci_init); diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index ca26628..e1859b8 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "otg_fsm.h" +#include "phy-fsm-usb.h" #include <linux/usb/otg.h> #include <linux/ioctl.h> diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index c520b35..7f45966 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -29,7 +29,7 @@ #include <linux/usb/gadget.h> #include <linux/usb/otg.h> -#include "phy-otg-fsm.h" +#include "phy-fsm-usb.h" /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) diff --git a/drivers/xen/events.c b/drivers/xen/events.c index a58ac43..5e8be46 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -348,7 +348,7 @@ static void init_evtchn_cpu_bindings(void) for_each_possible_cpu(i) memset(per_cpu(cpu_evtchn_mask, i), - (i == 0) ? ~0 : 0, sizeof(*per_cpu(cpu_evtchn_mask, i))); + (i == 0) ? ~0 : 0, NR_EVENT_CHANNELS/8); } static inline void clear_evtchn(int port) @@ -1493,8 +1493,10 @@ void rebind_evtchn_irq(int evtchn, int irq) /* Rebind an evtchn so that it gets delivered to a specific cpu */ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) { + struct shared_info *s = HYPERVISOR_shared_info; struct evtchn_bind_vcpu bind_vcpu; int evtchn = evtchn_from_irq(irq); + int masked; if (!VALID_EVTCHN(evtchn)) return -1; @@ -1511,6 +1513,12 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) bind_vcpu.vcpu = tcpu; /* + * Mask the event while changing the VCPU binding to prevent + * it being delivered on an unexpected VCPU. + */ + masked = sync_test_and_set_bit(evtchn, BM(s->evtchn_mask)); + + /* * If this fails, it usually just indicates that we're dealing with a * virq or IPI channel, which don't actually need to be rebound. Ignore * it, but don't do the xenlinux-level rebind in that case. @@ -1518,6 +1526,9 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) bind_evtchn_to_cpu(evtchn, tcpu); + if (!masked) + unmask_evtchn(evtchn); + return 0; } |