diff options
Diffstat (limited to 'drivers')
88 files changed, 3066 insertions, 966 deletions
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 541e18879965..528f6318ded1 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -180,9 +180,6 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio) BUG(); bio_endio(bio, -ENXIO); return 0; - } else if (bio->bi_rw & REQ_HARDBARRIER) { - bio_endio(bio, -EOPNOTSUPP); - return 0; } else if (bio->bi_io_vec == NULL) { printk(KERN_ERR "aoe: bi_io_vec is NULL\n"); BUG(); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 2cc4dda46279..a67d0a611a8a 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -113,6 +113,8 @@ static struct board_type products[] = { {0x409D0E11, "Smart Array 6400 EM", &SA5_access}, {0x40910E11, "Smart Array 6i", &SA5_access}, {0x3225103C, "Smart Array P600", &SA5_access}, + {0x3223103C, "Smart Array P800", &SA5_access}, + {0x3234103C, "Smart Array P400", &SA5_access}, {0x3235103C, "Smart Array P400i", &SA5_access}, {0x3211103C, "Smart Array E200i", &SA5_access}, {0x3212103C, "Smart Array E200", &SA5_access}, @@ -3753,7 +3755,7 @@ static void __devinit cciss_wait_for_mode_change_ack(ctlr_info_t *h) for (i = 0; i < MAX_CONFIG_WAIT; i++) { if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) break; - msleep(10); + usleep_range(10000, 20000); } } @@ -3937,10 +3939,9 @@ static int __devinit cciss_lookup_board_id(struct pci_dev *pdev, u32 *board_id) *board_id = ((subsystem_device_id << 16) & 0xffff0000) | subsystem_vendor_id; - for (i = 0; i < ARRAY_SIZE(products); i++) { + for (i = 0; i < ARRAY_SIZE(products); i++) if (*board_id == products[i].board_id) return i; - } dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x, ignoring.\n", *board_id); return -ENODEV; @@ -3971,18 +3972,31 @@ static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev, return -ENODEV; } -static int __devinit cciss_wait_for_board_ready(ctlr_info_t *h) +static int __devinit cciss_wait_for_board_state(struct pci_dev *pdev, + void __iomem *vaddr, int wait_for_ready) +#define BOARD_READY 1 +#define BOARD_NOT_READY 0 { - int i; + int i, iterations; u32 scratchpad; - for (i = 0; i < CCISS_BOARD_READY_ITERATIONS; i++) { - scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); - if (scratchpad == CCISS_FIRMWARE_READY) - return 0; + if (wait_for_ready) + iterations = CCISS_BOARD_READY_ITERATIONS; + else + iterations = CCISS_BOARD_NOT_READY_ITERATIONS; + + for (i = 0; i < iterations; i++) { + scratchpad = readl(vaddr + SA5_SCRATCHPAD_OFFSET); + if (wait_for_ready) { + if (scratchpad == CCISS_FIRMWARE_READY) + return 0; + } else { + if (scratchpad != CCISS_FIRMWARE_READY) + return 0; + } msleep(CCISS_BOARD_READY_POLL_INTERVAL_MSECS); } - dev_warn(&h->pdev->dev, "board not ready, timed out.\n"); + dev_warn(&pdev->dev, "board not ready, timed out.\n"); return -ENODEV; } @@ -4031,6 +4045,11 @@ static int __devinit cciss_find_cfgtables(ctlr_info_t *h) static void __devinit cciss_get_max_perf_mode_cmds(struct ctlr_info *h) { h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands)); + + /* Limit commands in memory limited kdump scenario. */ + if (reset_devices && h->max_commands > 32) + h->max_commands = 32; + if (h->max_commands < 16) { dev_warn(&h->pdev->dev, "Controller reports " "max supported commands of %d, an obvious lie. " @@ -4148,7 +4167,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *h) err = -ENOMEM; goto err_out_free_res; } - err = cciss_wait_for_board_ready(h); + err = cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY); if (err) goto err_out_free_res; err = cciss_find_cfgtables(h); @@ -4313,36 +4332,6 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u #define cciss_soft_reset_controller(p) cciss_message(p, 1, 0) #define cciss_noop(p) cciss_message(p, 3, 0) -static __devinit int cciss_reset_msi(struct pci_dev *pdev) -{ -/* the #defines are stolen from drivers/pci/msi.h. */ -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) - - int pos; - u16 control = 0; - - pos = pci_find_capability(pdev, PCI_CAP_ID_MSI); - if (pos) { - pci_read_config_word(pdev, msi_control_reg(pos), &control); - if (control & PCI_MSI_FLAGS_ENABLE) { - dev_info(&pdev->dev, "resetting MSI\n"); - pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE); - } - } - - pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); - if (pos) { - pci_read_config_word(pdev, msi_control_reg(pos), &control); - if (control & PCI_MSIX_FLAGS_ENABLE) { - dev_info(&pdev->dev, "resetting MSI-X\n"); - pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE); - } - } - - return 0; -} - static int cciss_controller_hard_reset(struct pci_dev *pdev, void * __iomem vaddr, bool use_doorbell) { @@ -4397,17 +4386,17 @@ static int cciss_controller_hard_reset(struct pci_dev *pdev, * states or using the doorbell register. */ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) { - u16 saved_config_space[32]; u64 cfg_offset; u32 cfg_base_addr; u64 cfg_base_addr_index; void __iomem *vaddr; unsigned long paddr; u32 misc_fw_support, active_transport; - int rc, i; + int rc; CfgTable_struct __iomem *cfgtable; bool use_doorbell; u32 board_id; + u16 command_register; /* For controllers as old a the p600, this is very nearly * the same thing as @@ -4417,14 +4406,6 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) * pci_set_power_state(pci_dev, PCI_D0); * pci_restore_state(pci_dev); * - * but we can't use these nice canned kernel routines on - * kexec, because they also check the MSI/MSI-X state in PCI - * configuration space and do the wrong thing when it is - * set/cleared. Also, the pci_save/restore_state functions - * violate the ordering requirements for restoring the - * configuration space from the CCISS document (see the - * comment below). So we roll our own .... - * * For controllers newer than the P600, the pci power state * method of resetting doesn't work so we have another way * using the doorbell register. @@ -4443,8 +4424,13 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) return -ENODEV; } - for (i = 0; i < 32; i++) - pci_read_config_word(pdev, 2*i, &saved_config_space[i]); + /* Save the PCI command register */ + pci_read_config_word(pdev, 4, &command_register); + /* Turn the board off. This is so that later pci_restore_state() + * won't turn the board on before the rest of config space is ready. + */ + pci_disable_device(pdev); + pci_save_state(pdev); /* find the first memory BAR, so we can find the cfg table */ rc = cciss_pci_find_memory_BAR(pdev, &paddr); @@ -4479,26 +4465,32 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell); if (rc) goto unmap_cfgtable; - - /* Restore the PCI configuration space. The Open CISS - * Specification says, "Restore the PCI Configuration - * Registers, offsets 00h through 60h. It is important to - * restore the command register, 16-bits at offset 04h, - * last. Do not restore the configuration status register, - * 16-bits at offset 06h." Note that the offset is 2*i. - */ - for (i = 0; i < 32; i++) { - if (i == 2 || i == 3) - continue; - pci_write_config_word(pdev, 2*i, saved_config_space[i]); + pci_restore_state(pdev); + rc = pci_enable_device(pdev); + if (rc) { + dev_warn(&pdev->dev, "failed to enable device.\n"); + goto unmap_cfgtable; } - wmb(); - pci_write_config_word(pdev, 4, saved_config_space[2]); + pci_write_config_word(pdev, 4, command_register); /* Some devices (notably the HP Smart Array 5i Controller) need a little pause here */ msleep(CCISS_POST_RESET_PAUSE_MSECS); + /* Wait for board to become not ready, then ready. */ + dev_info(&pdev->dev, "Waiting for board to become ready.\n"); + rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY); + if (rc) /* Don't bail, might be E500, etc. which can't be reset */ + dev_warn(&pdev->dev, + "failed waiting for board to become not ready\n"); + rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_READY); + if (rc) { + dev_warn(&pdev->dev, + "failed waiting for board to become ready\n"); + goto unmap_cfgtable; + } + dev_info(&pdev->dev, "board ready.\n"); + /* Controller should be in simple mode at this point. If it's not, * It means we're on one of those controllers which doesn't support * the doorbell reset method and on which the PCI power management reset @@ -4539,8 +4531,6 @@ static __devinit int cciss_init_reset_devices(struct pci_dev *pdev) return 0; /* just try to do the kdump anyhow. */ if (rc) return -ENODEV; - if (cciss_reset_msi(pdev)) - return -ENODEV; /* Now try to get the controller to respond to a no-op */ for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { @@ -4936,7 +4926,8 @@ static void __exit cciss_cleanup(void) } } kthread_stop(cciss_scan_thread); - remove_proc_entry("driver/cciss", NULL); + if (proc_cciss) + remove_proc_entry("driver/cciss", NULL); bus_unregister(&cciss_bus_type); } diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index ae340ffc8f81..4b8933d778f1 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -200,10 +200,14 @@ struct ctlr_info * the above. */ #define CCISS_BOARD_READY_WAIT_SECS (120) +#define CCISS_BOARD_NOT_READY_WAIT_SECS (10) #define CCISS_BOARD_READY_POLL_INTERVAL_MSECS (100) #define CCISS_BOARD_READY_ITERATIONS \ ((CCISS_BOARD_READY_WAIT_SECS * 1000) / \ CCISS_BOARD_READY_POLL_INTERVAL_MSECS) +#define CCISS_BOARD_NOT_READY_ITERATIONS \ + ((CCISS_BOARD_NOT_READY_WAIT_SECS * 1000) / \ + CCISS_BOARD_READY_POLL_INTERVAL_MSECS) #define CCISS_POST_RESET_PAUSE_MSECS (3000) #define CCISS_POST_RESET_NOOP_INTERVAL_MSECS (1000) #define CCISS_POST_RESET_NOOP_RETRIES (12) diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index ac04ef97eac2..ba95cba192be 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -78,11 +78,10 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev, init_completion(&md_io.event); md_io.error = 0; - if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags)) - rw |= REQ_HARDBARRIER; + if ((rw & WRITE) && !test_bit(MD_NO_FUA, &mdev->flags)) + rw |= REQ_FUA; rw |= REQ_UNPLUG | REQ_SYNC; - retry: bio = bio_alloc(GFP_NOIO, 1); bio->bi_bdev = bdev->md_bdev; bio->bi_sector = sector; @@ -100,17 +99,6 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev, wait_for_completion(&md_io.event); ok = bio_flagged(bio, BIO_UPTODATE) && md_io.error == 0; - /* check for unsupported barrier op. - * would rather check on EOPNOTSUPP, but that is not reliable. - * don't try again for ANY return value != 0 */ - if (unlikely((bio->bi_rw & REQ_HARDBARRIER) && !ok)) { - /* Try again with no barrier */ - dev_warn(DEV, "Barriers not supported on meta data device - disabling\n"); - set_bit(MD_NO_BARRIER, &mdev->flags); - rw &= ~REQ_HARDBARRIER; - bio_put(bio); - goto retry; - } out: bio_put(bio); return ok; @@ -284,18 +272,32 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused) u32 xor_sum = 0; if (!get_ldev(mdev)) { - dev_err(DEV, "get_ldev() failed in w_al_write_transaction\n"); + dev_err(DEV, + "disk is %s, cannot start al transaction (-%d +%d)\n", + drbd_disk_str(mdev->state.disk), evicted, new_enr); complete(&((struct update_al_work *)w)->event); return 1; } /* do we have to do a bitmap write, first? * TODO reduce maximum latency: * submit both bios, then wait for both, - * instead of doing two synchronous sector writes. */ + * instead of doing two synchronous sector writes. + * For now, we must not write the transaction, + * if we cannot write out the bitmap of the evicted extent. */ if (mdev->state.conn < C_CONNECTED && evicted != LC_FREE) drbd_bm_write_sect(mdev, evicted/AL_EXT_PER_BM_SECT); - mutex_lock(&mdev->md_io_mutex); /* protects md_io_page, al_tr_cycle, ... */ + /* The bitmap write may have failed, causing a state change. */ + if (mdev->state.disk < D_INCONSISTENT) { + dev_err(DEV, + "disk is %s, cannot write al transaction (-%d +%d)\n", + drbd_disk_str(mdev->state.disk), evicted, new_enr); + complete(&((struct update_al_work *)w)->event); + put_ldev(mdev); + return 1; + } + + mutex_lock(&mdev->md_io_mutex); /* protects md_io_buffer, al_tr_cycle, ... */ buffer = (struct al_transaction *)page_address(mdev->md_io_page); buffer->magic = __constant_cpu_to_be32(DRBD_MAGIC); @@ -739,7 +741,7 @@ void drbd_al_apply_to_bm(struct drbd_conf *mdev) unsigned int enr; unsigned long add = 0; char ppb[10]; - int i; + int i, tmp; wait_event(mdev->al_wait, lc_try_lock(mdev->act_log)); @@ -747,7 +749,9 @@ void drbd_al_apply_to_bm(struct drbd_conf *mdev) enr = lc_element_by_index(mdev->act_log, i)->lc_number; if (enr == LC_FREE) continue; - add += drbd_bm_ALe_set_all(mdev, enr); + tmp = drbd_bm_ALe_set_all(mdev, enr); + dynamic_dev_dbg(DEV, "AL: set %d bits in extent %u\n", tmp, enr); + add += tmp; } lc_unlock(mdev->act_log); diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 9bdcf4393c0a..1ea1a34e78b2 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -114,11 +114,11 @@ struct drbd_conf; #define D_ASSERT(exp) if (!(exp)) \ dev_err(DEV, "ASSERT( " #exp " ) in %s:%d\n", __FILE__, __LINE__) -#define ERR_IF(exp) if (({ \ - int _b = (exp) != 0; \ - if (_b) dev_err(DEV, "%s: (%s) in %s:%d\n", \ - __func__, #exp, __FILE__, __LINE__); \ - _b; \ +#define ERR_IF(exp) if (({ \ + int _b = (exp) != 0; \ + if (_b) dev_err(DEV, "ASSERT FAILED: %s: (%s) in %s:%d\n", \ + __func__, #exp, __FILE__, __LINE__); \ + _b; \ })) /* Defines to control fault insertion */ @@ -749,17 +749,12 @@ struct drbd_epoch { /* drbd_epoch flag bits */ enum { - DE_BARRIER_IN_NEXT_EPOCH_ISSUED, - DE_BARRIER_IN_NEXT_EPOCH_DONE, - DE_CONTAINS_A_BARRIER, DE_HAVE_BARRIER_NUMBER, - DE_IS_FINISHING, }; enum epoch_event { EV_PUT, EV_GOT_BARRIER_NR, - EV_BARRIER_DONE, EV_BECAME_LAST, EV_CLEANUP = 32, /* used as flag */ }; @@ -801,11 +796,6 @@ enum { __EE_CALL_AL_COMPLETE_IO, __EE_MAY_SET_IN_SYNC, - /* This epoch entry closes an epoch using a barrier. - * On sucessful completion, the epoch is released, - * and the P_BARRIER_ACK send. */ - __EE_IS_BARRIER, - /* In case a barrier failed, * we need to resubmit without the barrier flag. */ __EE_RESUBMITTED, @@ -820,7 +810,6 @@ enum { }; #define EE_CALL_AL_COMPLETE_IO (1<<__EE_CALL_AL_COMPLETE_IO) #define EE_MAY_SET_IN_SYNC (1<<__EE_MAY_SET_IN_SYNC) -#define EE_IS_BARRIER (1<<__EE_IS_BARRIER) #define EE_RESUBMITTED (1<<__EE_RESUBMITTED) #define EE_WAS_ERROR (1<<__EE_WAS_ERROR) #define EE_HAS_DIGEST (1<<__EE_HAS_DIGEST) @@ -843,16 +832,15 @@ enum { * Gets cleared when the state.conn * goes into C_CONNECTED state. */ WRITE_BM_AFTER_RESYNC, /* A kmalloc() during resync failed */ - NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */ CONSIDER_RESYNC, - MD_NO_BARRIER, /* meta data device does not support barriers, - so don't even try */ + MD_NO_FUA, /* Users wants us to not use FUA/FLUSH on meta data dev */ SUSPEND_IO, /* suspend application io */ BITMAP_IO, /* suspend application io; once no more io in flight, start bitmap io */ BITMAP_IO_QUEUED, /* Started bitmap IO */ - GO_DISKLESS, /* Disk failed, local_cnt reached zero, we are going diskless */ + GO_DISKLESS, /* Disk is being detached, on io-error or admin request. */ + WAS_IO_ERROR, /* Local disk failed returned IO error */ RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */ NET_CONGESTED, /* The data socket is congested */ @@ -947,7 +935,6 @@ enum write_ordering_e { WO_none, WO_drain_io, WO_bdev_flush, - WO_bio_barrier }; struct fifo_buffer { @@ -1281,6 +1268,7 @@ extern int drbd_bmio_set_n_write(struct drbd_conf *mdev); extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev); extern int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why); extern void drbd_go_diskless(struct drbd_conf *mdev); +extern void drbd_ldev_destroy(struct drbd_conf *mdev); /* Meta data layout @@ -1798,17 +1786,17 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, case EP_PASS_ON: if (!forcedetach) { if (__ratelimit(&drbd_ratelimit_state)) - dev_err(DEV, "Local IO failed in %s." - "Passing error on...\n", where); + dev_err(DEV, "Local IO failed in %s.\n", where); break; } /* NOTE fall through to detach case if forcedetach set */ case EP_DETACH: case EP_CALL_HELPER: + set_bit(WAS_IO_ERROR, &mdev->flags); if (mdev->state.disk > D_FAILED) { _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL); - dev_err(DEV, "Local IO failed in %s." - "Detaching...\n", where); + dev_err(DEV, + "Local IO failed in %s. Detaching...\n", where); } break; } @@ -1874,7 +1862,7 @@ static inline sector_t drbd_md_last_sector(struct drbd_backing_dev *bdev) static inline sector_t drbd_get_capacity(struct block_device *bdev) { /* return bdev ? get_capacity(bdev->bd_disk) : 0; */ - return bdev ? bdev->bd_inode->i_size >> 9 : 0; + return bdev ? i_size_read(bdev->bd_inode) >> 9 : 0; } /** @@ -2127,7 +2115,11 @@ static inline void put_ldev(struct drbd_conf *mdev) __release(local); D_ASSERT(i >= 0); if (i == 0) { + if (mdev->state.disk == D_DISKLESS) + /* even internal references gone, safe to destroy */ + drbd_ldev_destroy(mdev); if (mdev->state.disk == D_FAILED) + /* all application IO references gone. */ drbd_go_diskless(mdev); wake_up(&mdev->misc_wait); } @@ -2138,6 +2130,10 @@ static inline int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_stat { int io_allowed; + /* never get a reference while D_DISKLESS */ + if (mdev->state.disk == D_DISKLESS) + return 0; + atomic_inc(&mdev->local_cnt); io_allowed = (mdev->state.disk >= mins); if (!io_allowed) @@ -2406,12 +2402,12 @@ static inline void drbd_md_flush(struct drbd_conf *mdev) { int r; - if (test_bit(MD_NO_BARRIER, &mdev->flags)) + if (test_bit(MD_NO_FUA, &mdev->flags)) return; r = blkdev_issue_flush(mdev->ldev->md_bdev, GFP_KERNEL, NULL); if (r) { - set_bit(MD_NO_BARRIER, &mdev->flags); + set_bit(MD_NO_FUA, &mdev->flags); dev_err(DEV, "meta data flush failed with status %d, disabling md-flushes\n", r); } } diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 25c7a73c5062..6be5401d0e88 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -835,6 +835,15 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_TEAR_DOWN) ns.conn = os.conn; + /* we cannot fail (again) if we already detached */ + if (ns.disk == D_FAILED && os.disk == D_DISKLESS) + ns.disk = D_DISKLESS; + + /* if we are only D_ATTACHING yet, + * we can (and should) go directly to D_DISKLESS. */ + if (ns.disk == D_FAILED && os.disk == D_ATTACHING) + ns.disk = D_DISKLESS; + /* After C_DISCONNECTING only C_STANDALONE may follow */ if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE) ns.conn = os.conn; @@ -1056,7 +1065,15 @@ int __drbd_set_state(struct drbd_conf *mdev, !test_and_set_bit(CONFIG_PENDING, &mdev->flags)) set_bit(DEVICE_DYING, &mdev->flags); - mdev->state.i = ns.i; + /* if we are going -> D_FAILED or D_DISKLESS, grab one extra reference + * on the ldev here, to be sure the transition -> D_DISKLESS resp. + * drbd_ldev_destroy() won't happen before our corresponding + * after_state_ch works run, where we put_ldev again. */ + if ((os.disk != D_FAILED && ns.disk == D_FAILED) || + (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) + atomic_inc(&mdev->local_cnt); + + mdev->state = ns; wake_up(&mdev->misc_wait); wake_up(&mdev->state_wait); @@ -1268,7 +1285,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, if (test_bit(NEW_CUR_UUID, &mdev->flags)) { drbd_uuid_new_current(mdev); clear_bit(NEW_CUR_UUID, &mdev->flags); - drbd_md_sync(mdev); } spin_lock_irq(&mdev->req_lock); _drbd_set_state(_NS(mdev, susp_fen, 0), CS_VERBOSE, NULL); @@ -1365,63 +1381,64 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT) drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL, "set_n_write from invalidate"); - /* first half of local IO error */ - if (os.disk > D_FAILED && ns.disk == D_FAILED) { - enum drbd_io_error_p eh = EP_PASS_ON; + /* first half of local IO error, failure to attach, + * or administrative detach */ + if (os.disk != D_FAILED && ns.disk == D_FAILED) { + enum drbd_io_error_p eh; + int was_io_error; + /* corresponding get_ldev was in __drbd_set_state, to serialize + * our cleanup here with the transition to D_DISKLESS, + * so it is safe to dreference ldev here. */ + eh = mdev->ldev->dc.on_io_error; + was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags); + + /* current state still has to be D_FAILED, + * there is only one way out: to D_DISKLESS, + * and that may only happen after our put_ldev below. */ + if (mdev->state.disk != D_FAILED) + dev_err(DEV, + "ASSERT FAILED: disk is %s during detach\n", + drbd_disk_str(mdev->state.disk)); if (drbd_send_state(mdev)) - dev_warn(DEV, "Notified peer that my disk is broken.\n"); + dev_warn(DEV, "Notified peer that I am detaching my disk\n"); else - dev_err(DEV, "Sending state for drbd_io_error() failed\n"); + dev_err(DEV, "Sending state for detaching disk failed\n"); drbd_rs_cancel_all(mdev); - if (get_ldev_if_state(mdev, D_FAILED)) { - eh = mdev->ldev->dc.on_io_error; - put_ldev(mdev); - } - if (eh == EP_CALL_HELPER) + /* In case we want to get something to stable storage still, + * this may be the last chance. + * Following put_ldev may transition to D_DISKLESS. */ + drbd_md_sync(mdev); + put_ldev(mdev); + + if (was_io_error && eh == EP_CALL_HELPER) drbd_khelper(mdev, "local-io-error"); } + /* second half of local IO error, failure to attach, + * or administrative detach, + * after local_cnt references have reached zero again */ + if (os.disk != D_DISKLESS && ns.disk == D_DISKLESS) { + /* We must still be diskless, + * re-attach has to be serialized with this! */ + if (mdev->state.disk != D_DISKLESS) + dev_err(DEV, + "ASSERT FAILED: disk is %s while going diskless\n", + drbd_disk_str(mdev->state.disk)); - /* second half of local IO error handling, - * after local_cnt references have reached zero: */ - if (os.disk == D_FAILED && ns.disk == D_DISKLESS) { - mdev->rs_total = 0; - mdev->rs_failed = 0; - atomic_set(&mdev->rs_pending_cnt, 0); - } - - if (os.disk > D_DISKLESS && ns.disk == D_DISKLESS) { - /* We must still be diskless, - * re-attach has to be serialized with this! */ - if (mdev->state.disk != D_DISKLESS) - dev_err(DEV, - "ASSERT FAILED: disk is %s while going diskless\n", - drbd_disk_str(mdev->state.disk)); + mdev->rs_total = 0; + mdev->rs_failed = 0; + atomic_set(&mdev->rs_pending_cnt, 0); - /* we cannot assert local_cnt == 0 here, as get_ldev_if_state - * will inc/dec it frequently. Since we became D_DISKLESS, no - * one has touched the protected members anymore, though, so we - * are safe to free them here. */ if (drbd_send_state(mdev)) - dev_warn(DEV, "Notified peer that I detached my disk.\n"); + dev_warn(DEV, "Notified peer that I'm now diskless.\n"); else - dev_err(DEV, "Sending state for detach failed\n"); - - lc_destroy(mdev->resync); - mdev->resync = NULL; - lc_destroy(mdev->act_log); - mdev->act_log = NULL; - __no_warn(local, - drbd_free_bc(mdev->ldev); - mdev->ldev = NULL;); - - if (mdev->md_io_tmpp) { - __free_page(mdev->md_io_tmpp); - mdev->md_io_tmpp = NULL; - } + dev_err(DEV, "Sending state for being diskless failed\n"); + /* corresponding get_ldev in __drbd_set_state + * this may finaly trigger drbd_ldev_destroy. */ + put_ldev(mdev); } /* Disks got bigger while they were detached */ @@ -2772,11 +2789,6 @@ void drbd_init_set_defaults(struct drbd_conf *mdev) drbd_set_defaults(mdev); - /* for now, we do NOT yet support it, - * even though we start some framework - * to eventually support barriers */ - set_bit(NO_BARRIER_SUPP, &mdev->flags); - atomic_set(&mdev->ap_bio_cnt, 0); atomic_set(&mdev->ap_pending_cnt, 0); atomic_set(&mdev->rs_pending_cnt, 0); @@ -2842,7 +2854,7 @@ void drbd_init_set_defaults(struct drbd_conf *mdev) drbd_thread_init(mdev, &mdev->asender, drbd_asender); mdev->agreed_pro_version = PRO_VERSION_MAX; - mdev->write_ordering = WO_bio_barrier; + mdev->write_ordering = WO_bdev_flush; mdev->resync_wenr = LC_FREE; } @@ -2899,7 +2911,6 @@ void drbd_mdev_cleanup(struct drbd_conf *mdev) D_ASSERT(list_empty(&mdev->resync_work.list)); D_ASSERT(list_empty(&mdev->unplug_work.list)); D_ASSERT(list_empty(&mdev->go_diskless.list)); - } @@ -3660,6 +3671,8 @@ void drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local) get_random_bytes(&val, sizeof(u64)); _drbd_uuid_set(mdev, UI_CURRENT, val); + /* get it to stable storage _now_ */ + drbd_md_sync(mdev); } void drbd_uuid_set_bm(struct drbd_conf *mdev, u64 val) __must_hold(local) @@ -3756,19 +3769,31 @@ static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused) return 1; } +void drbd_ldev_destroy(struct drbd_conf *mdev) +{ + lc_destroy(mdev->resync); + mdev->resync = NULL; + lc_destroy(mdev->act_log); + mdev->act_log = NULL; + __no_warn(local, + drbd_free_bc(mdev->ldev); + mdev->ldev = NULL;); + + if (mdev->md_io_tmpp) { + __free_page(mdev->md_io_tmpp); + mdev->md_io_tmpp = NULL; + } + clear_bit(GO_DISKLESS, &mdev->flags); +} + static int w_go_diskless(struct drbd_conf *mdev, struct drbd_work *w, int unused) { D_ASSERT(mdev->state.disk == D_FAILED); /* we cannot assert local_cnt == 0 here, as get_ldev_if_state will * inc/dec it frequently. Once we are D_DISKLESS, no one will touch - * the protected members anymore, though, so in the after_state_ch work - * it will be safe to free them. */ + * the protected members anymore, though, so once put_ldev reaches zero + * again, it will be safe to free them. */ drbd_force_state(mdev, NS(disk, D_DISKLESS)); - /* We need to wait for return of references checked out while we still - * have been D_FAILED, though (drbd_md_sync, bitmap io). */ - wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); - - clear_bit(GO_DISKLESS, &mdev->flags); return 1; } @@ -3777,9 +3802,6 @@ void drbd_go_diskless(struct drbd_conf *mdev) D_ASSERT(mdev->state.disk == D_FAILED); if (!test_and_set_bit(GO_DISKLESS, &mdev->flags)) drbd_queue_work(&mdev->data.work, &mdev->go_diskless); - /* don't drbd_queue_work_front, - * we need to serialize with the after_state_ch work - * of the -> D_FAILED transition. */ } /** diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 87925e97e613..29e5c70e4e26 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -870,6 +870,11 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp retcode = ERR_DISK_CONFIGURED; goto fail; } + /* It may just now have detached because of IO error. Make sure + * drbd_ldev_destroy is done already, we may end up here very fast, + * e.g. if someone calls attach from the on-io-error handler, + * to realize a "hot spare" feature (not that I'd recommend that) */ + wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); /* allocation not in the IO path, cqueue thread context */ nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL); @@ -1098,9 +1103,9 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp /* Reset the "barriers don't work" bits here, then force meta data to * be written, to ensure we determine if barriers are supported. */ if (nbc->dc.no_md_flush) - set_bit(MD_NO_BARRIER, &mdev->flags); + set_bit(MD_NO_FUA, &mdev->flags); else - clear_bit(MD_NO_BARRIER, &mdev->flags); + clear_bit(MD_NO_FUA, &mdev->flags); /* Point of no return reached. * Devices and memory are no longer released by error cleanup below. @@ -1112,8 +1117,8 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp nbc = NULL; resync_lru = NULL; - mdev->write_ordering = WO_bio_barrier; - drbd_bump_write_ordering(mdev, WO_bio_barrier); + mdev->write_ordering = WO_bdev_flush; + drbd_bump_write_ordering(mdev, WO_bdev_flush); if (drbd_md_test_flag(mdev->ldev, MDF_CRASHED_PRIMARY)) set_bit(CRASHED_PRIMARY, &mdev->flags); @@ -1262,7 +1267,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp force_diskless_dec: put_ldev(mdev); force_diskless: - drbd_force_state(mdev, NS(disk, D_DISKLESS)); + drbd_force_state(mdev, NS(disk, D_FAILED)); drbd_md_sync(mdev); release_bdev2_fail: if (nbc) @@ -1285,10 +1290,19 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp return 0; } +/* Detaching the disk is a process in multiple stages. First we need to lock + * out application IO, in-flight IO, IO stuck in drbd_al_begin_io. + * Then we transition to D_DISKLESS, and wait for put_ldev() to return all + * internal references as well. + * Only then we have finally detached. */ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) { + drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */ reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS)); + if (mdev->state.disk == D_DISKLESS) + wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt)); + drbd_resume_io(mdev); return 0; } @@ -1953,7 +1967,6 @@ static int drbd_nl_resume_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp if (test_bit(NEW_CUR_UUID, &mdev->flags)) { drbd_uuid_new_current(mdev); clear_bit(NEW_CUR_UUID, &mdev->flags); - drbd_md_sync(mdev); } drbd_suspend_io(mdev); reply->ret_code = drbd_request_state(mdev, NS3(susp, 0, susp_nod, 0, susp_fen, 0)); diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c index ad325c5d0ce1..7e6ac307e2de 100644 --- a/drivers/block/drbd/drbd_proc.c +++ b/drivers/block/drbd/drbd_proc.c @@ -158,7 +158,6 @@ static int drbd_seq_show(struct seq_file *seq, void *v) [WO_none] = 'n', [WO_drain_io] = 'd', [WO_bdev_flush] = 'f', - [WO_bio_barrier] = 'b', }; seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n", diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index efd6169acf2f..d299fe9e78c8 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -49,11 +49,6 @@ #include "drbd_vli.h" -struct flush_work { - struct drbd_work w; - struct drbd_epoch *epoch; -}; - enum finish_epoch { FE_STILL_LIVE, FE_DESTROYED, @@ -66,16 +61,6 @@ static int drbd_do_auth(struct drbd_conf *mdev); static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *, struct drbd_epoch *, enum epoch_event); static int e_end_block(struct drbd_conf *, struct drbd_work *, int); -static struct drbd_epoch *previous_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch) -{ - struct drbd_epoch *prev; - spin_lock(&mdev->epoch_lock); - prev = list_entry(epoch->list.prev, struct drbd_epoch, list); - if (prev == epoch || prev == mdev->current_epoch) - prev = NULL; - spin_unlock(&mdev->epoch_lock); - return prev; -} #define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN) @@ -981,7 +966,7 @@ static int drbd_recv_header(struct drbd_conf *mdev, enum drbd_packets *cmd, unsi return TRUE; } -static enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch) +static void drbd_flush(struct drbd_conf *mdev) { int rv; @@ -997,24 +982,6 @@ static enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct d } put_ldev(mdev); } - - return drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE); -} - -static int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel) -{ - struct flush_work *fw = (struct flush_work *)w; - struct drbd_epoch *epoch = fw->epoch; - - kfree(w); - - if (!test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags)) - drbd_flush_after_epoch(mdev, epoch); - - drbd_may_finish_epoch(mdev, epoch, EV_PUT | - (mdev->state.conn < C_CONNECTED ? EV_CLEANUP : 0)); - - return 1; } /** @@ -1027,15 +994,13 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch, enum epoch_event ev) { - int finish, epoch_size; + int epoch_size; struct drbd_epoch *next_epoch; - int schedule_flush = 0; enum finish_epoch rv = FE_STILL_LIVE; spin_lock(&mdev->epoch_lock); do { next_epoch = NULL; - finish = 0; epoch_size = atomic_read(&epoch->epoch_size); @@ -1045,16 +1010,6 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, break; case EV_GOT_BARRIER_NR: set_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags); - - /* Special case: If we just switched from WO_bio_barrier to - WO_bdev_flush we should not finish the current epoch */ - if (test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags) && epoch_size == 1 && - mdev->write_ordering != WO_bio_barrier && - epoch == mdev->current_epoch) - clear_bit(DE_CONTAINS_A_BARRIER, &epoch->flags); - break; - case EV_BARRIER_DONE: - set_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags); break; case EV_BECAME_LAST: /* nothing to do*/ @@ -1063,23 +1018,7 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, if (epoch_size != 0 && atomic_read(&epoch->active) == 0 && - test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags) && - epoch->list.prev == &mdev->current_epoch->list && - !test_bit(DE_IS_FINISHING, &epoch->flags)) { - /* Nearly all conditions are met to finish that epoch... */ - if (test_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags) || - mdev->write_ordering == WO_none || - (epoch_size == 1 && test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) || - ev & EV_CLEANUP) { - finish = 1; - set_bit(DE_IS_FINISHING, &epoch->flags); - } else if (!test_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags) && - mdev->write_ordering == WO_bio_barrier) { - atomic_inc(&epoch->active); - schedule_flush = 1; - } - } - if (finish) { + test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags)) { if (!(ev & EV_CLEANUP)) { spin_unlock(&mdev->epoch_lock); drbd_send_b_ack(mdev, epoch->barrier_nr, epoch_size); @@ -1102,6 +1041,7 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, /* atomic_set(&epoch->active, 0); is already zero */ if (rv == FE_STILL_LIVE) rv = FE_RECYCLED; + wake_up(&mdev->ee_wait); } } @@ -1113,22 +1053,6 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev, spin_unlock(&mdev->epoch_lock); - if (schedule_flush) { - struct flush_work *fw; - fw = kmalloc(sizeof(*fw), GFP_ATOMIC); - if (fw) { - fw->w.cb = w_flush; - fw->epoch = epoch; - drbd_queue_work(&mdev->data.work, &fw->w); - } else { - dev_warn(DEV, "Could not kmalloc a flush_work obj\n"); - set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); - /* That is not a recursion, only one level */ - drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE); - drbd_may_finish_epoch(mdev, epoch, EV_PUT); - } - } - return rv; } @@ -1144,19 +1068,16 @@ void drbd_bump_write_ordering(struct drbd_conf *mdev, enum write_ordering_e wo) [WO_none] = "none", [WO_drain_io] = "drain", [WO_bdev_flush] = "flush", - [WO_bio_barrier] = "barrier", }; pwo = mdev->write_ordering; wo = min(pwo, wo); - if (wo == WO_bio_barrier && mdev->ldev->dc.no_disk_barrier) - wo = WO_bdev_flush; if (wo == WO_bdev_flush && mdev->ldev->dc.no_disk_flush) wo = WO_drain_io; if (wo == WO_drain_io && mdev->ldev->dc.no_disk_drain) wo = WO_none; mdev->write_ordering = wo; - if (pwo != mdev->write_ordering || wo == WO_bio_barrier) + if (pwo != mdev->write_ordering || wo == WO_bdev_flush) dev_info(DEV, "Method to ensure write ordering: %s\n", write_ordering_str[mdev->write_ordering]); } @@ -1192,7 +1113,7 @@ next_bio: bio->bi_sector = sector; bio->bi_bdev = mdev->ldev->backing_bdev; /* we special case some flags in the multi-bio case, see below - * (REQ_UNPLUG, REQ_HARDBARRIER) */ + * (REQ_UNPLUG) */ bio->bi_rw = rw; bio->bi_private = e; bio->bi_end_io = drbd_endio_sec; @@ -1226,11 +1147,6 @@ next_bio: bio->bi_rw &= ~REQ_UNPLUG; drbd_generic_make_request(mdev, fault_type, bio); - - /* strip off REQ_HARDBARRIER, - * unless it is the first or last bio */ - if (bios && bios->bi_next) - bios->bi_rw &= ~REQ_HARDBARRIER; } while (bios); maybe_kick_lo(mdev); return 0; @@ -1244,45 +1160,9 @@ fail: return -ENOMEM; } -/** - * w_e_reissue() - Worker callback; Resubmit a bio, without REQ_HARDBARRIER set - * @mdev: DRBD device. - * @w: work object. - * @cancel: The connection will be closed anyways (unused in this callback) - */ -int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __releases(local) -{ - struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; - /* We leave DE_CONTAINS_A_BARRIER and EE_IS_BARRIER in place, - (and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch) - so that we can finish that epoch in drbd_may_finish_epoch(). - That is necessary if we already have a long chain of Epochs, before - we realize that REQ_HARDBARRIER is actually not supported */ - - /* As long as the -ENOTSUPP on the barrier is reported immediately - that will never trigger. If it is reported late, we will just - print that warning and continue correctly for all future requests - with WO_bdev_flush */ - if (previous_epoch(mdev, e->epoch)) - dev_warn(DEV, "Write ordering was not enforced (one time event)\n"); - - /* we still have a local reference, - * get_ldev was done in receive_Data. */ - - e->w.cb = e_end_block; - if (drbd_submit_ee(mdev, e, WRITE, DRBD_FAULT_DT_WR) != 0) { - /* drbd_submit_ee fails for one reason only: - * if was not able to allocate sufficient bios. - * requeue, try again later. */ - e->w.cb = w_e_reissue; - drbd_queue_work(&mdev->data.work, &e->w); - } - return 1; -} - static int receive_Barrier(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned int data_size) { - int rv, issue_flush; + int rv; struct p_barrier *p = &mdev->data.rbuf.barrier; struct drbd_epoch *epoch; @@ -1300,44 +1180,40 @@ static int receive_Barrier(struct drbd_conf *mdev, enum drbd_packets cmd, unsign * Therefore we must send the barrier_ack after the barrier request was * completed. */ switch (mdev->write_ordering) { - case WO_bio_barrier: case WO_none: if (rv == FE_RECYCLED) return TRUE; - break; + + /* receiver context, in the writeout path of the other node. + * avoid potential distributed deadlock */ + epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO); + if (epoch) + break; + else + dev_warn(DEV, "Allocation of an epoch failed, slowing down\n"); + /* Fall through */ case WO_bdev_flush: case WO_drain_io: - if (rv == FE_STILL_LIVE) { - set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &mdev->current_epoch->flags); - drbd_wait_ee_list_empty(mdev, &mdev->active_ee); - rv = drbd_flush_after_epoch(mdev, mdev->current_epoch); - } - if (rv == FE_RECYCLED) - return TRUE; - - /* The asender will send all the ACKs and barrier ACKs out, since - all EEs moved from the active_ee to the done_ee. We need to - provide a new epoch object for the EEs that come in soon */ - break; - } - - /* receiver context, in the writeout path of the other node. - * avoid potential distributed deadlock */ - epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO); - if (!epoch) { - dev_warn(DEV, "Allocation of an epoch failed, slowing down\n"); - issue_flush = !test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &mdev->current_epoch->flags); drbd_wait_ee_list_empty(mdev, &mdev->active_ee); - if (issue_flush) { - rv = drbd_flush_after_epoch(mdev, mdev->current_epoch); - if (rv == FE_RECYCLED) - return TRUE; + drbd_flush(mdev); + + if (atomic_read(&mdev->current_epoch->epoch_size)) { + epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO); + if (epoch) + break; } - drbd_wait_ee_list_empty(mdev, &mdev->done_ee); + epoch = mdev->current_epoch; + wait_event(mdev->ee_wait, atomic_read(&epoch->epoch_size) == 0); + + D_ASSERT(atomic_read(&epoch->active) == 0); + D_ASSERT(epoch->flags == 0); return TRUE; + default: + dev_err(DEV, "Strangeness in mdev->write_ordering %d\n", mdev->write_ordering); + return FALSE; } epoch->flags = 0; @@ -1652,15 +1528,8 @@ static int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int cancel) { struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w; sector_t sector = e->sector; - struct drbd_epoch *epoch; int ok = 1, pcmd; - if (e->flags & EE_IS_BARRIER) { - epoch = previous_epoch(mdev, e->epoch); - if (epoch) - drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE + (cancel ? EV_CLEANUP : 0)); - } - if (mdev->net_conf->wire_protocol == DRBD_PROT_C) { if (likely((e->flags & EE_WAS_ERROR) == 0)) { pcmd = (mdev->state.conn >= C_SYNC_SOURCE && @@ -1817,27 +1686,6 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned e->epoch = mdev->current_epoch; atomic_inc(&e->epoch->epoch_size); atomic_inc(&e->epoch->active); - - if (mdev->write_ordering == WO_bio_barrier && atomic_read(&e->epoch->epoch_size) == 1) { - struct drbd_epoch *epoch; - /* Issue a barrier if we start a new epoch, and the previous epoch - was not a epoch containing a single request which already was - a Barrier. */ - epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list); - if (epoch == e->epoch) { - set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); - rw |= REQ_HARDBARRIER; - e->flags |= EE_IS_BARRIER; - } else { - if (atomic_read(&epoch->epoch_size) > 1 || - !test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) { - set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); - set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); - rw |= REQ_HARDBARRIER; - e->flags |= EE_IS_BARRIER; - } - } - } spin_unlock(&mdev->epoch_lock); dp_flags = be32_to_cpu(p->dp_flags); @@ -1995,10 +1843,11 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned break; } - if (mdev->state.pdsk == D_DISKLESS) { + if (mdev->state.pdsk < D_INCONSISTENT) { /* In case we have the only disk of the cluster, */ drbd_set_out_of_sync(mdev, e->sector, e->size); e->flags |= EE_CALL_AL_COMPLETE_IO; + e->flags &= ~EE_MAY_SET_IN_SYNC; drbd_al_begin_io(mdev, e->sector); } @@ -3362,7 +3211,7 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned if (ns.conn == C_MASK) { ns.conn = C_CONNECTED; if (mdev->state.disk == D_NEGOTIATING) { - drbd_force_state(mdev, NS(disk, D_DISKLESS)); + drbd_force_state(mdev, NS(disk, D_FAILED)); } else if (peer_state.disk == D_NEGOTIATING) { dev_err(DEV, "Disk attach process on the peer node was aborted.\n"); peer_state.disk = D_DISKLESS; diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 9e91a2545fc8..11a75d32a2e2 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -258,7 +258,7 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m) if (!hlist_unhashed(&req->colision)) hlist_del(&req->colision); else - D_ASSERT((s & RQ_NET_MASK) == 0); + D_ASSERT((s & (RQ_NET_MASK & ~RQ_NET_DONE)) == 0); /* for writes we need to do some extra housekeeping */ if (rw == WRITE) @@ -813,7 +813,8 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio) mdev->state.conn >= C_CONNECTED)); if (!(local || remote) && !is_susp(mdev->state)) { - dev_err(DEV, "IO ERROR: neither local nor remote disk\n"); + if (__ratelimit(&drbd_ratelimit_state)) + dev_err(DEV, "IO ERROR: neither local nor remote disk\n"); goto fail_free_complete; } @@ -942,12 +943,21 @@ allocate_barrier: if (local) { req->private_bio->bi_bdev = mdev->ldev->backing_bdev; - if (FAULT_ACTIVE(mdev, rw == WRITE ? DRBD_FAULT_DT_WR - : rw == READ ? DRBD_FAULT_DT_RD - : DRBD_FAULT_DT_RA)) + /* State may have changed since we grabbed our reference on the + * mdev->ldev member. Double check, and short-circuit to endio. + * In case the last activity log transaction failed to get on + * stable storage, and this is a WRITE, we may not even submit + * this bio. */ + if (get_ldev(mdev)) { + if (FAULT_ACTIVE(mdev, rw == WRITE ? DRBD_FAULT_DT_WR + : rw == READ ? DRBD_FAULT_DT_RD + : DRBD_FAULT_DT_RA)) + bio_endio(req->private_bio, -EIO); + else + generic_make_request(req->private_bio); + put_ldev(mdev); + } else bio_endio(req->private_bio, -EIO); - else - generic_make_request(req->private_bio); } /* we need to plug ALWAYS since we possibly need to kick lo_dev. @@ -1022,20 +1032,6 @@ int drbd_make_request_26(struct request_queue *q, struct bio *bio) return 0; } - /* Reject barrier requests if we know the underlying device does - * not support them. - * XXX: Need to get this info from peer as well some how so we - * XXX: reject if EITHER side/data/metadata area does not support them. - * - * because of those XXX, this is not yet enabled, - * i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit. - */ - if (unlikely(bio->bi_rw & REQ_HARDBARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags)) { - /* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */ - bio_endio(bio, -EOPNOTSUPP); - return 0; - } - /* * what we "blindly" assume: */ diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 108d58015cd1..b0551ba7ad0c 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -102,12 +102,6 @@ void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local) put_ldev(mdev); } -static int is_failed_barrier(int ee_flags) -{ - return (ee_flags & (EE_IS_BARRIER|EE_WAS_ERROR|EE_RESUBMITTED)) - == (EE_IS_BARRIER|EE_WAS_ERROR); -} - /* writes on behalf of the partner, or resync writes, * "submitted" by the receiver, final stage. */ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(local) @@ -119,21 +113,6 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo int is_syncer_req; int do_al_complete_io; - /* if this is a failed barrier request, disable use of barriers, - * and schedule for resubmission */ - if (is_failed_barrier(e->flags)) { - drbd_bump_write_ordering(mdev, WO_bdev_flush); - spin_lock_irqsave(&mdev->req_lock, flags); - list_del(&e->w.list); - e->flags = (e->flags & ~EE_WAS_ERROR) | EE_RESUBMITTED; - e->w.cb = w_e_reissue; - /* put_ldev actually happens below, once we come here again. */ - __release(local); - spin_unlock_irqrestore(&mdev->req_lock, flags); - drbd_queue_work(&mdev->data.work, &e->w); - return; - } - D_ASSERT(e->block_id != ID_VACANT); /* after we moved e to done_ee, @@ -925,7 +904,7 @@ out: drbd_md_sync(mdev); if (test_and_clear_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags)) { - dev_warn(DEV, "Writing the whole bitmap, due to failed kmalloc\n"); + dev_info(DEV, "Writing the whole bitmap\n"); drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL, "write from resync_finished"); } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 1e5284ef65fa..7ea0bea2f7e3 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -481,12 +481,6 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) if (bio_rw(bio) == WRITE) { struct file *file = lo->lo_backing_file; - /* REQ_HARDBARRIER is deprecated */ - if (bio->bi_rw & REQ_HARDBARRIER) { - ret = -EOPNOTSUPP; - goto out; - } - if (bio->bi_rw & REQ_FLUSH) { ret = vfs_fsync(file, 0); if (unlikely(ret && ret != -EINVAL)) { diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 06e2812ba124..255035cfc88a 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -289,8 +289,6 @@ static int blkif_queue_request(struct request *req) ring_req->operation = rq_data_dir(req) ? BLKIF_OP_WRITE : BLKIF_OP_READ; - if (req->cmd_flags & REQ_HARDBARRIER) - ring_req->operation = BLKIF_OP_WRITE_BARRIER; ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg); BUG_ON(ring_req->nr_segments > BLKIF_MAX_SEGMENTS_PER_REQUEST); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 6b6760ea2435..9272c38dd3c6 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1210,14 +1210,14 @@ static void gen6_write_entry(dma_addr_t addr, unsigned int entry, unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; u32 pte_flags; - if (type_mask == AGP_USER_UNCACHED_MEMORY) + if (type_mask == AGP_USER_MEMORY) pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID; else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) { - pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; + pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID; if (gfdt) pte_flags |= GEN6_PTE_GFDT; } else { /* set 'normal'/'cached' to LLC by default */ - pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID; + pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; if (gfdt) pte_flags |= GEN6_PTE_GFDT; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index dcbeb98f195a..f7af91cb273d 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -276,7 +276,7 @@ static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, struct drm_crtc *tmp; int crtc_mask = 1; - WARN(!crtc, "checking null crtc?"); + WARN(!crtc, "checking null crtc?\n"); dev = crtc->dev; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c1a26217a530..a245d17165ae 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -240,7 +240,7 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, .addr = DDC_ADDR, .flags = I2C_M_RD, .len = len, - .buf = buf + start, + .buf = buf, } }; @@ -253,7 +253,7 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, static u8 * drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { - int i, j = 0; + int i, j = 0, valid_extensions = 0; u8 *block, *new; if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) @@ -280,14 +280,28 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) for (j = 1; j <= block[0x7e]; j++) { for (i = 0; i < 4; i++) { - if (drm_do_probe_ddc_edid(adapter, block, j, - EDID_LENGTH)) + if (drm_do_probe_ddc_edid(adapter, + block + (valid_extensions + 1) * EDID_LENGTH, + j, EDID_LENGTH)) goto out; - if (drm_edid_block_valid(block + j * EDID_LENGTH)) + if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH)) { + valid_extensions++; break; + } } if (i == 4) - goto carp; + dev_warn(connector->dev->dev, + "%s: Ignoring invalid EDID block %d.\n", + drm_get_connector_name(connector), j); + } + + if (valid_extensions != block[0x7e]) { + block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; + block[0x7e] = valid_extensions; + new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); + if (!new) + goto out; + block = new; } return block; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3467dd420760..80745f85902c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -44,7 +44,7 @@ unsigned int i915_fbpercrtc = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); unsigned int i915_powersave = 1; -module_param_named(powersave, i915_powersave, int, 0400); +module_param_named(powersave, i915_powersave, int, 0600); unsigned int i915_lvds_downclock = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2c2c19b6285e..90414ae86afc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1321,6 +1321,7 @@ static inline void i915_write(struct drm_i915_private *dev_priv, u32 reg, #define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type) #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT) +#define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX) #define PRIMARY_RINGBUFFER_SIZE (128*1024) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8eb8453208b5..ef188e391406 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2172,7 +2172,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) static int i915_ring_idle(struct drm_device *dev, struct intel_ring_buffer *ring) { - if (list_empty(&ring->gpu_write_list)) + if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list)) return 0; i915_gem_flush_ring(dev, NULL, ring, @@ -2190,9 +2190,7 @@ i915_gpu_idle(struct drm_device *dev) int ret; lists_empty = (list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - list_empty(&dev_priv->bsd_ring.active_list) && - list_empty(&dev_priv->blt_ring.active_list)); + list_empty(&dev_priv->mm.active_list)); if (lists_empty) return 0; @@ -3108,7 +3106,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, * write domain */ if (obj->write_domain && - obj->write_domain != obj->pending_read_domains) { + (obj->write_domain != obj->pending_read_domains || + obj_priv->ring != ring)) { flush_domains |= obj->write_domain; invalidate_domains |= obj->pending_read_domains & ~obj->write_domain; @@ -3497,6 +3496,52 @@ i915_gem_execbuffer_pin(struct drm_device *dev, return 0; } +static int +i915_gem_execbuffer_move_to_gpu(struct drm_device *dev, + struct drm_file *file, + struct intel_ring_buffer *ring, + struct drm_gem_object **objects, + int count) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret, i; + + /* Zero the global flush/invalidate flags. These + * will be modified as new domains are computed + * for each object + */ + dev->invalidate_domains = 0; + dev->flush_domains = 0; + dev_priv->mm.flush_rings = 0; + for (i = 0; i < count; i++) + i915_gem_object_set_to_gpu_domain(objects[i], ring); + + if (dev->invalidate_domains | dev->flush_domains) { +#if WATCH_EXEC + DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", + __func__, + dev->invalidate_domains, + dev->flush_domains); +#endif + i915_gem_flush(dev, file, + dev->invalidate_domains, + dev->flush_domains, + dev_priv->mm.flush_rings); + } + + for (i = 0; i < count; i++) { + struct drm_i915_gem_object *obj = to_intel_bo(objects[i]); + /* XXX replace with semaphores */ + if (obj->ring && ring != obj->ring) { + ret = i915_gem_object_wait_rendering(&obj->base, true); + if (ret) + return ret; + } + } + + return 0; +} + /* Throttle our rendering by waiting until the ring has completed our requests * emitted over 20 msec ago. * @@ -3757,33 +3802,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } - /* Zero the global flush/invalidate flags. These - * will be modified as new domains are computed - * for each object - */ - dev->invalidate_domains = 0; - dev->flush_domains = 0; - dev_priv->mm.flush_rings = 0; - - for (i = 0; i < args->buffer_count; i++) { - struct drm_gem_object *obj = object_list[i]; - - /* Compute new gpu domains and update invalidate/flush */ - i915_gem_object_set_to_gpu_domain(obj, ring); - } - - if (dev->invalidate_domains | dev->flush_domains) { -#if WATCH_EXEC - DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", - __func__, - dev->invalidate_domains, - dev->flush_domains); -#endif - i915_gem_flush(dev, file, - dev->invalidate_domains, - dev->flush_domains, - dev_priv->mm.flush_rings); - } + ret = i915_gem_execbuffer_move_to_gpu(dev, file, ring, + object_list, args->buffer_count); + if (ret) + goto err; for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; @@ -4043,8 +4065,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) alignment = i915_gem_get_gtt_alignment(obj); if (obj_priv->gtt_offset & (alignment - 1)) { WARN(obj_priv->pin_count, - "bo is already pinned with incorrect alignment:" - " offset=%x, req.alignment=%x\n", + "bo is already pinned with incorrect alignment: offset=%x, req.alignment=%x\n", obj_priv->gtt_offset, alignment); ret = i915_gem_object_unbind(obj); if (ret) @@ -4856,17 +4877,24 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, struct drm_file *file_priv) { struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); - void *obj_addr; - int ret; - char __user *user_data; + void *vaddr = obj_priv->phys_obj->handle->vaddr + args->offset; + char __user *user_data = (char __user *) (uintptr_t) args->data_ptr; - user_data = (char __user *) (uintptr_t) args->data_ptr; - obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset; + DRM_DEBUG_DRIVER("vaddr %p, %lld\n", vaddr, args->size); - DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size); - ret = copy_from_user(obj_addr, user_data, args->size); - if (ret) - return -EFAULT; + if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) { + unsigned long unwritten; + + /* The physical object once assigned is fixed for the lifetime + * of the obj, so we can safely drop the lock and continue + * to access vaddr. + */ + mutex_unlock(&dev->struct_mutex); + unwritten = copy_from_user(vaddr, user_data, args->size); + mutex_lock(&dev->struct_mutex); + if (unwritten) + return -EFAULT; + } drm_agp_chipset_flush(dev); return 0; @@ -4900,9 +4928,7 @@ i915_gpu_is_active(struct drm_device *dev) int lists_empty; lists_empty = list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - list_empty(&dev_priv->bsd_ring.active_list) && - list_empty(&dev_priv->blt_ring.active_list); + list_empty(&dev_priv->mm.active_list); return !lists_empty; } diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 43a4013f53fa..d8ae7d1d0cc6 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -165,9 +165,7 @@ i915_gem_evict_everything(struct drm_device *dev) lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - list_empty(&dev_priv->bsd_ring.active_list) && - list_empty(&dev_priv->blt_ring.active_list)); + list_empty(&dev_priv->mm.active_list)); if (lists_empty) return -ENOSPC; @@ -184,9 +182,7 @@ i915_gem_evict_everything(struct drm_device *dev) lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - list_empty(&dev_priv->bsd_ring.active_list) && - list_empty(&dev_priv->blt_ring.active_list)); + list_empty(&dev_priv->mm.active_list)); BUG_ON(!lists_empty); return 0; diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 989c19d2d959..454c064f8ef7 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -862,8 +862,10 @@ int i915_restore_state(struct drm_device *dev) /* Clock gating state */ intel_init_clock_gating(dev); - if (HAS_PCH_SPLIT(dev)) + if (HAS_PCH_SPLIT(dev)) { ironlake_enable_drps(dev); + intel_init_emon(dev); + } /* Cache mode state */ I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 990f065374b2..48d8fd686ea9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1681,6 +1681,37 @@ static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) udelay(500); } +static void intel_fdi_normal_train(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + u32 reg, temp; + + /* enable normal train */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + if (HAS_PCH_CPT(dev)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_NORMAL_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE; + } + I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); + + /* wait one idle pattern time */ + POSTING_READ(reg); + udelay(1000); +} + /* The FDI link training functions for ILK/Ibexpeak. */ static void ironlake_fdi_link_train(struct drm_crtc *crtc) { @@ -1767,27 +1798,6 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) DRM_DEBUG_KMS("FDI train done\n"); - /* enable normal train */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; - I915_WRITE(reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = I915_READ(reg); - if (HAS_PCH_CPT(dev)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_NORMAL_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE; - } - I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); - - /* wait one idle pattern time */ - POSTING_READ(reg); - udelay(1000); } static const int const snb_b_fdi_train_param [] = { @@ -2090,6 +2100,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe))); I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); + intel_fdi_normal_train(crtc); + /* For PCH DP, enable TRANS_DP_CTL */ if (HAS_PCH_CPT(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { @@ -2200,9 +2212,10 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) udelay(100); /* Ironlake workaround, disable clock pointer after downing FDI */ - I915_WRITE(FDI_RX_CHICKEN(pipe), - I915_READ(FDI_RX_CHICKEN(pipe) & - ~FDI_RX_PHASE_SYNC_POINTER_ENABLE)); + if (HAS_PCH_IBX(dev)) + I915_WRITE(FDI_RX_CHICKEN(pipe), + I915_READ(FDI_RX_CHICKEN(pipe) & + ~FDI_RX_PHASE_SYNC_POINTER_ENABLE)); /* still set train pattern 1 */ reg = FDI_TX_CTL(pipe); @@ -5581,20 +5594,19 @@ void ironlake_enable_drps(struct drm_device *dev) fmin = (rgvmodectl & MEMMODE_FMIN_MASK); fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT; - fstart = fmax; vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT; - dev_priv->fmax = fstart; /* IPS callback will increase this */ + dev_priv->fmax = fmax; /* IPS callback will increase this */ dev_priv->fstart = fstart; - dev_priv->max_delay = fmax; + dev_priv->max_delay = fstart; dev_priv->min_delay = fmin; dev_priv->cur_delay = fstart; - DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin, - fstart); + DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", + fmax, fmin, fstart); I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 891f4f1d63b1..c8e005553310 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1517,7 +1517,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) status = connector_status_connected; } - return bit; + return status; } /** diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9af9f86a8765..21551fe74541 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -296,6 +296,7 @@ extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, extern void intel_init_clock_gating(struct drm_device *dev); extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); +extern void intel_init_emon(struct drm_device *dev); extern int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj, diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index f1a649990ea9..4324a326f98e 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -481,11 +481,8 @@ static int intel_lvds_get_modes(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_display_mode *mode; - if (intel_lvds->edid) { - drm_mode_connector_update_edid_property(connector, - intel_lvds->edid); + if (intel_lvds->edid) return drm_add_edid_modes(connector, intel_lvds->edid); - } mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); if (mode == 0) @@ -939,7 +936,16 @@ void intel_lvds_init(struct drm_device *dev) */ intel_lvds->edid = drm_get_edid(connector, &dev_priv->gmbus[pin].adapter); - + if (intel_lvds->edid) { + if (drm_add_edid_modes(connector, + intel_lvds->edid)) { + drm_mode_connector_update_edid_property(connector, + intel_lvds->edid); + } else { + kfree(intel_lvds->edid); + intel_lvds->edid = NULL; + } + } if (!intel_lvds->edid) { /* Didn't get an EDID, so * Set wide sync ranges so we get all modes diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 917c7dc3cd6b..9b0d9a867aea 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -512,6 +512,6 @@ int intel_opregion_setup(struct drm_device *dev) return 0; err_out: - iounmap(opregion->header); + iounmap(base); return err; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index afb96d25219a..02ff0a481f47 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -946,7 +946,9 @@ static int check_overlay_src(struct drm_device *dev, { int uv_hscale = uv_hsubsampling(rec->flags); int uv_vscale = uv_vsubsampling(rec->flags); - u32 stride_mask, depth, tmp; + u32 stride_mask; + int depth; + u32 tmp; /* check src dimensions */ if (IS_845G(dev) || IS_I830(dev)) { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 09f2dc353ae2..b83306f9244b 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -177,7 +177,7 @@ static int init_ring_common(struct drm_device *dev, I915_WRITE_CTL(ring, ((ring->gem_object->size - PAGE_SIZE) & RING_NR_PAGES) - | RING_NO_REPORT | RING_VALID); + | RING_REPORT_64K | RING_VALID); head = I915_READ_HEAD(ring) & HEAD_ADDR; /* If the head is still not zero, the ring is dead */ @@ -654,6 +654,10 @@ void intel_cleanup_ring_buffer(struct drm_device *dev, i915_gem_object_unpin(ring->gem_object); drm_gem_object_unreference(ring->gem_object); ring->gem_object = NULL; + + if (ring->cleanup) + ring->cleanup(ring); + cleanup_status_page(dev, ring); } @@ -688,6 +692,17 @@ int intel_wait_ring_buffer(struct drm_device *dev, { unsigned long end; drm_i915_private_t *dev_priv = dev->dev_private; + u32 head; + + head = intel_read_status_page(ring, 4); + if (head) { + ring->head = head & HEAD_ADDR; + ring->space = ring->head - (ring->tail + 8); + if (ring->space < 0) + ring->space += ring->size; + if (ring->space >= n) + return 0; + } trace_i915_ring_wait_begin (dev); end = jiffies + 3 * HZ; @@ -854,19 +869,125 @@ blt_ring_put_user_irq(struct drm_device *dev, /* do nothing */ } + +/* Workaround for some stepping of SNB, + * each time when BLT engine ring tail moved, + * the first command in the ring to be parsed + * should be MI_BATCH_BUFFER_START + */ +#define NEED_BLT_WORKAROUND(dev) \ + (IS_GEN6(dev) && (dev->pdev->revision < 8)) + +static inline struct drm_i915_gem_object * +to_blt_workaround(struct intel_ring_buffer *ring) +{ + return ring->private; +} + +static int blt_ring_init(struct drm_device *dev, + struct intel_ring_buffer *ring) +{ + if (NEED_BLT_WORKAROUND(dev)) { + struct drm_i915_gem_object *obj; + u32 __iomem *ptr; + int ret; + + obj = to_intel_bo(i915_gem_alloc_object(dev, 4096)); + if (obj == NULL) + return -ENOMEM; + + ret = i915_gem_object_pin(&obj->base, 4096); + if (ret) { + drm_gem_object_unreference(&obj->base); + return ret; + } + + ptr = kmap(obj->pages[0]); + iowrite32(MI_BATCH_BUFFER_END, ptr); + iowrite32(MI_NOOP, ptr+1); + kunmap(obj->pages[0]); + + ret = i915_gem_object_set_to_gtt_domain(&obj->base, false); + if (ret) { + i915_gem_object_unpin(&obj->base); + drm_gem_object_unreference(&obj->base); + return ret; + } + + ring->private = obj; + } + + return init_ring_common(dev, ring); +} + +static void blt_ring_begin(struct drm_device *dev, + struct intel_ring_buffer *ring, + int num_dwords) +{ + if (ring->private) { + intel_ring_begin(dev, ring, num_dwords+2); + intel_ring_emit(dev, ring, MI_BATCH_BUFFER_START); + intel_ring_emit(dev, ring, to_blt_workaround(ring)->gtt_offset); + } else + intel_ring_begin(dev, ring, 4); +} + +static void blt_ring_flush(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 invalidate_domains, + u32 flush_domains) +{ + blt_ring_begin(dev, ring, 4); + intel_ring_emit(dev, ring, MI_FLUSH_DW); + intel_ring_emit(dev, ring, 0); + intel_ring_emit(dev, ring, 0); + intel_ring_emit(dev, ring, 0); + intel_ring_advance(dev, ring); +} + +static u32 +blt_ring_add_request(struct drm_device *dev, + struct intel_ring_buffer *ring, + u32 flush_domains) +{ + u32 seqno = i915_gem_get_seqno(dev); + + blt_ring_begin(dev, ring, 4); + intel_ring_emit(dev, ring, MI_STORE_DWORD_INDEX); + intel_ring_emit(dev, ring, + I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + intel_ring_emit(dev, ring, seqno); + intel_ring_emit(dev, ring, MI_USER_INTERRUPT); + intel_ring_advance(dev, ring); + + DRM_DEBUG_DRIVER("%s %d\n", ring->name, seqno); + return seqno; +} + +static void blt_ring_cleanup(struct intel_ring_buffer *ring) +{ + if (!ring->private) + return; + + i915_gem_object_unpin(ring->private); + drm_gem_object_unreference(ring->private); + ring->private = NULL; +} + static const struct intel_ring_buffer gen6_blt_ring = { .name = "blt ring", .id = RING_BLT, .mmio_base = BLT_RING_BASE, .size = 32 * PAGE_SIZE, - .init = init_ring_common, + .init = blt_ring_init, .write_tail = ring_write_tail, - .flush = gen6_ring_flush, - .add_request = ring_add_request, + .flush = blt_ring_flush, + .add_request = blt_ring_add_request, .get_seqno = ring_status_page_get_seqno, .user_irq_get = blt_ring_get_user_irq, .user_irq_put = blt_ring_put_user_irq, .dispatch_gem_execbuffer = gen6_ring_dispatch_gem_execbuffer, + .cleanup = blt_ring_cleanup, }; int intel_init_render_ring_buffer(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index a05aff0e5764..3126c2681983 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -63,6 +63,7 @@ struct intel_ring_buffer { struct drm_i915_gem_execbuffer2 *exec, struct drm_clip_rect *cliprects, uint64_t exec_offset); + void (*cleanup)(struct intel_ring_buffer *ring); /** * List of objects currently involved in rendering from the @@ -98,6 +99,8 @@ struct intel_ring_buffer { wait_queue_head_t irq_queue; drm_local_map_t map; + + void *private; }; static inline u32 diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index f12a5b3ec050..488c36c8f5e6 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -2033,7 +2033,7 @@ int evergreen_irq_set(struct radeon_device *rdev) u32 grbm_int_cntl = 0; if (!rdev->irq.installed) { - WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); + WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); return -EINVAL; } /* don't enable anything if the ih is disabled */ @@ -2295,6 +2295,7 @@ restart_ih: case 0: /* D1 vblank */ if (disp_int & LB_D1_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int &= ~LB_D1_VBLANK_INTERRUPT; DRM_DEBUG("IH: D1 vblank\n"); @@ -2316,6 +2317,7 @@ restart_ih: case 0: /* D2 vblank */ if (disp_int_cont & LB_D2_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; DRM_DEBUG("IH: D2 vblank\n"); @@ -2337,6 +2339,7 @@ restart_ih: case 0: /* D3 vblank */ if (disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 2); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; DRM_DEBUG("IH: D3 vblank\n"); @@ -2358,6 +2361,7 @@ restart_ih: case 0: /* D4 vblank */ if (disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 3); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; DRM_DEBUG("IH: D4 vblank\n"); @@ -2379,6 +2383,7 @@ restart_ih: case 0: /* D5 vblank */ if (disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 4); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; DRM_DEBUG("IH: D5 vblank\n"); @@ -2400,6 +2405,7 @@ restart_ih: case 0: /* D6 vblank */ if (disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 5); + rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; DRM_DEBUG("IH: D6 vblank\n"); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 0e8f28a68927..8e10aa9f74b0 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -442,7 +442,7 @@ int r100_pci_gart_init(struct radeon_device *rdev) int r; if (rdev->gart.table.ram.ptr) { - WARN(1, "R100 PCI GART already initialized.\n"); + WARN(1, "R100 PCI GART already initialized\n"); return 0; } /* Initialize common gart structure */ @@ -516,7 +516,7 @@ int r100_irq_set(struct radeon_device *rdev) uint32_t tmp = 0; if (!rdev->irq.installed) { - WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); + WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); WREG32(R_000040_GEN_INT_CNTL, 0); return -EINVAL; } diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 34527e600fe9..cde1d3480d93 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -91,7 +91,7 @@ int rv370_pcie_gart_init(struct radeon_device *rdev) int r; if (rdev->gart.table.vram.robj) { - WARN(1, "RV370 PCIE GART already initialized.\n"); + WARN(1, "RV370 PCIE GART already initialized\n"); return 0; } /* Initialize common gart structure */ diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 33952a12f0a3..0f806cc7dc75 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -97,14 +97,8 @@ u32 rv6xx_get_temp(struct radeon_device *rdev) { u32 temp = (RREG32(CG_THERMAL_STATUS) & ASIC_T_MASK) >> ASIC_T_SHIFT; - u32 actual_temp = 0; - if ((temp >> 7) & 1) - actual_temp = 0; - else - actual_temp = (temp >> 1) & 0xff; - - return actual_temp * 1000; + return temp * 1000; } void r600_pm_get_dynpm_state(struct radeon_device *rdev) @@ -919,7 +913,7 @@ int r600_pcie_gart_init(struct radeon_device *rdev) int r; if (rdev->gart.table.vram.robj) { - WARN(1, "R600 PCIE GART already initialized.\n"); + WARN(1, "R600 PCIE GART already initialized\n"); return 0; } /* Initialize common gart structure */ @@ -2995,7 +2989,7 @@ int r600_irq_set(struct radeon_device *rdev) u32 hdmi1, hdmi2; if (!rdev->irq.installed) { - WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); + WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); return -EINVAL; } /* don't enable anything if the ih is disabled */ diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 04cac7ec9039..87ead090c7d5 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -526,8 +526,6 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (crev < 2) return false; - router.valid = false; - obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + @@ -624,6 +622,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (connector_type == DRM_MODE_CONNECTOR_Unknown) continue; + router.ddc_valid = false; + router.cd_valid = false; for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { uint8_t grph_obj_id, grph_obj_num, grph_obj_type; @@ -647,9 +647,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) usDeviceTag)); } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { - router.valid = false; for (k = 0; k < router_obj->ucNumberOfObjects; k++) { - u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID); + u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) (ctx->bios + data_offset + @@ -657,6 +656,7 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_I2C_RECORD *i2c_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; + ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) (ctx->bios + data_offset + @@ -690,10 +690,18 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) record; - router.valid = true; - router.mux_type = ddc_path->ucMuxType; - router.mux_control_pin = ddc_path->ucMuxControlPin; - router.mux_state = ddc_path->ucMuxState[enum_id]; + router.ddc_valid = true; + router.ddc_mux_type = ddc_path->ucMuxType; + router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; + router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; + break; + case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: + cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) + record; + router.cd_valid = true; + router.cd_mux_type = cd_path->ucMuxType; + router.cd_mux_control_pin = cd_path->ucMuxControlPin; + router.cd_mux_state = cd_path->ucMuxState[enum_id]; break; } record = (ATOM_COMMON_RECORD_HEADER *) @@ -860,7 +868,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE; struct radeon_router router; - router.valid = false; + router.ddc_valid = false; + router.cd_valid = false; bios_connectors = kzalloc(bc_size, GFP_KERNEL); if (!bios_connectors) diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 4dac4b0a02ee..fe6c74780f18 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -183,13 +183,13 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, continue; if (priority == true) { - DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); - DRM_INFO("in favor of %s\n", drm_get_connector_name(connector)); + DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); + DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector)); conflict->status = connector_status_disconnected; radeon_connector_update_scratch_regs(conflict, connector_status_disconnected); } else { - DRM_INFO("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); - DRM_INFO("in favor of %s\n", drm_get_connector_name(conflict)); + DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); + DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict)); current_status = connector_status_disconnected; } break; @@ -432,13 +432,13 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, mode->vdisplay == native_mode->vdisplay) { *native_mode = *mode; drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V); - DRM_INFO("Determined LVDS native mode details from EDID\n"); + DRM_DEBUG_KMS("Determined LVDS native mode details from EDID\n"); break; } } } if (!native_mode->clock) { - DRM_INFO("No LVDS native mode details, disabling RMX\n"); + DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n"); radeon_encoder->rmx_type = RMX_OFF; } } @@ -1116,7 +1116,7 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->shared_ddc = true; shared_ddc = true; } - if (radeon_connector->router_bus && router->valid && + if (radeon_connector->router_bus && router->ddc_valid && (radeon_connector->router.router_id == router->router_id)) { radeon_connector->shared_ddc = false; shared_ddc = false; @@ -1136,7 +1136,7 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; radeon_connector->router = *router; - if (router->valid) { + if (router->ddc_valid || router->cd_valid) { radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info); if (!radeon_connector->router_bus) goto failed; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0383631da69c..1df4dc6c063c 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -315,10 +315,14 @@ static void radeon_print_display_setup(struct drm_device *dev) radeon_connector->ddc_bus->rec.en_data_reg, radeon_connector->ddc_bus->rec.y_clk_reg, radeon_connector->ddc_bus->rec.y_data_reg); - if (radeon_connector->router_bus) + if (radeon_connector->router.ddc_valid) DRM_INFO(" DDC Router 0x%x/0x%x\n", - radeon_connector->router.mux_control_pin, - radeon_connector->router.mux_state); + radeon_connector->router.ddc_mux_control_pin, + radeon_connector->router.ddc_mux_state); + if (radeon_connector->router.cd_valid) + DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", + radeon_connector->router.cd_mux_control_pin, + radeon_connector->router.cd_mux_state); } else { if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || connector->connector_type == DRM_MODE_CONNECTOR_DVII || @@ -398,8 +402,8 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) int ret = 0; /* on hw with routers, select right port */ - if (radeon_connector->router.valid) - radeon_router_select_port(radeon_connector); + if (radeon_connector->router.ddc_valid) + radeon_router_select_ddc_port(radeon_connector); if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { @@ -432,8 +436,8 @@ static int radeon_ddc_dump(struct drm_connector *connector) int ret = 0; /* on hw with routers, select right port */ - if (radeon_connector->router.valid) - radeon_router_select_port(radeon_connector); + if (radeon_connector->router.ddc_valid) + radeon_router_select_ddc_port(radeon_connector); if (!radeon_connector->ddc_bus) return -1; diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index ae58b6849a2e..f678257c42e6 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -1520,6 +1520,7 @@ radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connec static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); if (radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) { @@ -1531,6 +1532,13 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) radeon_atom_output_lock(encoder, true); radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + /* select the clock/data port if it uses a router */ + if (connector) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + if (radeon_connector->router.cd_valid) + radeon_router_select_cd_port(radeon_connector); + } + /* this is needed for the pll/ss setup to work correctly in some cases */ atombios_set_encoder_crtc_source(encoder); } @@ -1547,6 +1555,23 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder) struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig; + + /* check for pre-DCE3 cards with shared encoders; + * can't really use the links individually, so don't disable + * the encoder if it's in use by another connector + */ + if (!ASIC_IS_DCE3(rdev)) { + struct drm_encoder *other_encoder; + struct radeon_encoder *other_radeon_encoder; + + list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { + other_radeon_encoder = to_radeon_encoder(other_encoder); + if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) && + drm_helper_encoder_in_use(other_encoder)) + goto disable_done; + } + } + radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); switch (radeon_encoder->encoder_id) { @@ -1586,6 +1611,7 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder) break; } +disable_done: if (radeon_encoder_is_digital(encoder)) { if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) r600_hdmi_disable(encoder); diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 216392d0353b..daacb281dfaf 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -240,7 +240,8 @@ retry: */ if (seq == rdev->fence_drv.last_seq && radeon_gpu_is_lockup(rdev)) { /* good news we believe it's a lockup */ - WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", fence->seq, seq); + WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", + fence->seq, seq); /* FIXME: what should we do ? marking everyone * as signaled for now */ diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 6a13ee38a5b9..0cfbba02c4d0 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -53,8 +53,8 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector) }; /* on hw with routers, select right port */ - if (radeon_connector->router.valid) - radeon_router_select_port(radeon_connector); + if (radeon_connector->router.ddc_valid) + radeon_router_select_ddc_port(radeon_connector); ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); if (ret == 2) @@ -1084,26 +1084,51 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus, addr, val); } -/* router switching */ -void radeon_router_select_port(struct radeon_connector *radeon_connector) +/* ddc router switching */ +void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector) { u8 val; - if (!radeon_connector->router.valid) + if (!radeon_connector->router.ddc_valid) return; radeon_i2c_get_byte(radeon_connector->router_bus, radeon_connector->router.i2c_addr, 0x3, &val); - val &= radeon_connector->router.mux_control_pin; + val &= ~radeon_connector->router.ddc_mux_control_pin; radeon_i2c_put_byte(radeon_connector->router_bus, radeon_connector->router.i2c_addr, 0x3, val); radeon_i2c_get_byte(radeon_connector->router_bus, radeon_connector->router.i2c_addr, 0x1, &val); - val &= radeon_connector->router.mux_control_pin; - val |= radeon_connector->router.mux_state; + val &= ~radeon_connector->router.ddc_mux_control_pin; + val |= radeon_connector->router.ddc_mux_state; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, val); +} + +/* clock/data router switching */ +void radeon_router_select_cd_port(struct radeon_connector *radeon_connector) +{ + u8 val; + + if (!radeon_connector->router.cd_valid) + return; + + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, &val); + val &= ~radeon_connector->router.cd_mux_control_pin; + radeon_i2c_put_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x3, val); + radeon_i2c_get_byte(radeon_connector->router_bus, + radeon_connector->router.i2c_addr, + 0x1, &val); + val &= ~radeon_connector->router.cd_mux_control_pin; + val |= radeon_connector->router.cd_mux_state; radeon_i2c_put_byte(radeon_connector->router_bus, radeon_connector->router.i2c_addr, 0x1, val); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 92457163d070..680f57644e86 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -401,13 +401,19 @@ struct radeon_hpd { }; struct radeon_router { - bool valid; u32 router_id; struct radeon_i2c_bus_rec i2c_info; u8 i2c_addr; - u8 mux_type; - u8 mux_control_pin; - u8 mux_state; + /* i2c mux */ + bool ddc_valid; + u8 ddc_mux_type; + u8 ddc_mux_control_pin; + u8 ddc_mux_state; + /* clock/data mux */ + bool cd_valid; + u8 cd_mux_type; + u8 cd_mux_control_pin; + u8 cd_mux_state; }; struct radeon_connector { @@ -488,7 +494,8 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, u8 slave_addr, u8 addr, u8 val); -extern void radeon_router_select_port(struct radeon_connector *radeon_connector); +extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector); +extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector); extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index d7ab91416410..8eb183466015 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -102,6 +102,8 @@ int radeon_bo_create(struct radeon_device *rdev, struct drm_gem_object *gobj, type = ttm_bo_type_device; } *bo_ptr = NULL; + +retry: bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL); if (bo == NULL) return -ENOMEM; @@ -109,8 +111,6 @@ int radeon_bo_create(struct radeon_device *rdev, struct drm_gem_object *gobj, bo->gobj = gobj; bo->surface_reg = -1; INIT_LIST_HEAD(&bo->list); - -retry: radeon_ttm_placement_from_domain(bo, domain); /* Kernel allocation are uninterruptible */ mutex_lock(&rdev->vram_mutex); diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index fe95bb35317e..01c2c736a1da 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -689,7 +689,8 @@ static int radeon_ttm_backend_bind(struct ttm_backend *backend, gtt = container_of(backend, struct radeon_ttm_backend, backend); gtt->offset = bo_mem->start << PAGE_SHIFT; if (!gtt->num_pages) { - WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", gtt->num_pages, bo_mem, backend); + WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", + gtt->num_pages, bo_mem, backend); } r = radeon_gart_bind(gtt->rdev, gtt->offset, gtt->num_pages, gtt->pages); diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index f683e51a2a06..5512e4e5e636 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -78,7 +78,7 @@ int rs400_gart_init(struct radeon_device *rdev) int r; if (rdev->gart.table.ram.ptr) { - WARN(1, "RS400 GART already initialized.\n"); + WARN(1, "RS400 GART already initialized\n"); return 0; } /* Check gart size */ diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index b091a1f6fa4e..f1c6e02c2e6b 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -375,7 +375,7 @@ int rs600_gart_init(struct radeon_device *rdev) int r; if (rdev->gart.table.vram.robj) { - WARN(1, "RS600 GART already initialized.\n"); + WARN(1, "RS600 GART already initialized\n"); return 0; } /* Initialize common gart structure */ @@ -505,7 +505,7 @@ int rs600_irq_set(struct radeon_device *rdev) ~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1); if (!rdev->irq.installed) { - WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); + WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); WREG32(R_000040_GEN_INT_CNTL, 0); return -EINVAL; } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a1cb783c7131..3ca77dc03915 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -27,14 +27,6 @@ /* * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> */ -/* Notes: - * - * We store bo pointer in drm_mm_node struct so we know which bo own a - * specific node. There is no protection on the pointer, thus to make - * sure things don't go berserk you have to access this pointer while - * holding the global lru lock and make sure anytime you free a node you - * reset the pointer to NULL. - */ #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" @@ -45,6 +37,7 @@ #include <linux/mm.h> #include <linux/file.h> #include <linux/module.h> +#include <asm/atomic.h> #define TTM_ASSERT_LOCKED(param) #define TTM_DEBUG(fmt, arg...) @@ -452,6 +445,11 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) ttm_bo_mem_put(bo, &bo->mem); atomic_set(&bo->reserved, 0); + + /* + * Make processes trying to reserve really pick it up. + */ + smp_mb__after_atomic_dec(); wake_up_all(&bo->event_queue); } @@ -460,7 +458,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_global *glob = bo->glob; struct ttm_bo_driver *driver; - void *sync_obj; + void *sync_obj = NULL; void *sync_obj_arg; int put_count; int ret; @@ -495,17 +493,20 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) spin_lock(&glob->lru_lock); } queue: - sync_obj = bo->sync_obj; - sync_obj_arg = bo->sync_obj_arg; driver = bdev->driver; + if (bo->sync_obj) + sync_obj = driver->sync_obj_ref(bo->sync_obj); + sync_obj_arg = bo->sync_obj_arg; kref_get(&bo->list_kref); list_add_tail(&bo->ddestroy, &bdev->ddestroy); spin_unlock(&glob->lru_lock); spin_unlock(&bo->lock); - if (sync_obj) + if (sync_obj) { driver->sync_obj_flush(sync_obj, sync_obj_arg); + driver->sync_obj_unref(&sync_obj); + } schedule_delayed_work(&bdev->wq, ((HZ / 100) < 1) ? 1 : HZ / 100); } @@ -822,7 +823,6 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; - struct ttm_bo_global *glob = bdev->glob; struct ttm_mem_type_manager *man = &bdev->man[mem_type]; int ret; @@ -832,12 +832,6 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, return ret; if (mem->mm_node) break; - spin_lock(&glob->lru_lock); - if (list_empty(&man->lru)) { - spin_unlock(&glob->lru_lock); - break; - } - spin_unlock(&glob->lru_lock); ret = ttm_mem_evict_first(bdev, mem_type, interruptible, no_wait_reserve, no_wait_gpu); if (unlikely(ret != 0)) @@ -1125,35 +1119,9 @@ EXPORT_SYMBOL(ttm_bo_validate); int ttm_bo_check_placement(struct ttm_buffer_object *bo, struct ttm_placement *placement) { - int i; + BUG_ON((placement->fpfn || placement->lpfn) && + (bo->mem.num_pages > (placement->lpfn - placement->fpfn))); - if (placement->fpfn || placement->lpfn) { - if (bo->mem.num_pages > (placement->lpfn - placement->fpfn)) { - printk(KERN_ERR TTM_PFX "Page number range to small " - "Need %lu pages, range is [%u, %u]\n", - bo->mem.num_pages, placement->fpfn, - placement->lpfn); - return -EINVAL; - } - } - for (i = 0; i < placement->num_placement; i++) { - if (!capable(CAP_SYS_ADMIN)) { - if (placement->placement[i] & TTM_PL_FLAG_NO_EVICT) { - printk(KERN_ERR TTM_PFX "Need to be root to " - "modify NO_EVICT status.\n"); - return -EINVAL; - } - } - } - for (i = 0; i < placement->num_busy_placement; i++) { - if (!capable(CAP_SYS_ADMIN)) { - if (placement->busy_placement[i] & TTM_PL_FLAG_NO_EVICT) { - printk(KERN_ERR TTM_PFX "Need to be root to " - "modify NO_EVICT status.\n"); - return -EINVAL; - } - } - } return 0; } @@ -1176,6 +1144,10 @@ int ttm_bo_init(struct ttm_bo_device *bdev, num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (num_pages == 0) { printk(KERN_ERR TTM_PFX "Illegal buffer object size.\n"); + if (destroy) + (*destroy)(bo); + else + kfree(bo); return -EINVAL; } bo->destroy = destroy; @@ -1369,18 +1341,9 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, int ret = -EINVAL; struct ttm_mem_type_manager *man; - if (type >= TTM_NUM_MEM_TYPES) { - printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", type); - return ret; - } - + BUG_ON(type >= TTM_NUM_MEM_TYPES); man = &bdev->man[type]; - if (man->has_type) { - printk(KERN_ERR TTM_PFX - "Memory manager already initialized for type %d\n", - type); - return ret; - } + BUG_ON(man->has_type); ret = bdev->driver->init_mem_type(bdev, type, man); if (ret) @@ -1389,13 +1352,6 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, ret = 0; if (type != TTM_PL_SYSTEM) { - if (!p_size) { - printk(KERN_ERR TTM_PFX - "Zero size memory manager type %d\n", - type); - return ret; - } - ret = (*man->func->init)(man, p_size); if (ret) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index 7410c190c891..038e947d00f9 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA + * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -31,20 +31,29 @@ #include "ttm/ttm_module.h" #include "ttm/ttm_bo_driver.h" #include "ttm/ttm_placement.h" -#include <linux/jiffies.h> +#include "drm_mm.h" #include <linux/slab.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/file.h> +#include <linux/spinlock.h> #include <linux/module.h> +/** + * Currently we use a spinlock for the lock, but a mutex *may* be + * more appropriate to reduce scheduling latency if the range manager + * ends up with very fragmented allocation patterns. + */ + +struct ttm_range_manager { + struct drm_mm mm; + spinlock_t lock; +}; + static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_mem_reg *mem) { - struct ttm_bo_global *glob = man->bdev->glob; - struct drm_mm *mm = man->priv; + struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; + struct drm_mm *mm = &rman->mm; struct drm_mm_node *node = NULL; unsigned long lpfn; int ret; @@ -57,19 +66,19 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, if (unlikely(ret)) return ret; - spin_lock(&glob->lru_lock); + spin_lock(&rman->lock); node = drm_mm_search_free_in_range(mm, mem->num_pages, mem->page_alignment, placement->fpfn, lpfn, 1); if (unlikely(node == NULL)) { - spin_unlock(&glob->lru_lock); + spin_unlock(&rman->lock); return 0; } node = drm_mm_get_block_atomic_range(node, mem->num_pages, - mem->page_alignment, - placement->fpfn, - lpfn); - spin_unlock(&glob->lru_lock); + mem->page_alignment, + placement->fpfn, + lpfn); + spin_unlock(&rman->lock); } while (node == NULL); mem->mm_node = node; @@ -80,12 +89,12 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) { - struct ttm_bo_global *glob = man->bdev->glob; + struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; if (mem->mm_node) { - spin_lock(&glob->lru_lock); + spin_lock(&rman->lock); drm_mm_put_block(mem->mm_node); - spin_unlock(&glob->lru_lock); + spin_unlock(&rman->lock); mem->mm_node = NULL; } } @@ -93,49 +102,49 @@ static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, static int ttm_bo_man_init(struct ttm_mem_type_manager *man, unsigned long p_size) { - struct drm_mm *mm; + struct ttm_range_manager *rman; int ret; - mm = kzalloc(sizeof(*mm), GFP_KERNEL); - if (!mm) + rman = kzalloc(sizeof(*rman), GFP_KERNEL); + if (!rman) return -ENOMEM; - ret = drm_mm_init(mm, 0, p_size); + ret = drm_mm_init(&rman->mm, 0, p_size); if (ret) { - kfree(mm); + kfree(rman); return ret; } - man->priv = mm; + spin_lock_init(&rman->lock); + man->priv = rman; return 0; } static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man) { - struct ttm_bo_global *glob = man->bdev->glob; - struct drm_mm *mm = man->priv; - int ret = 0; + struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; + struct drm_mm *mm = &rman->mm; - spin_lock(&glob->lru_lock); + spin_lock(&rman->lock); if (drm_mm_clean(mm)) { drm_mm_takedown(mm); - kfree(mm); + spin_unlock(&rman->lock); + kfree(rman); man->priv = NULL; - } else - ret = -EBUSY; - spin_unlock(&glob->lru_lock); - return ret; + return 0; + } + spin_unlock(&rman->lock); + return -EBUSY; } static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, const char *prefix) { - struct ttm_bo_global *glob = man->bdev->glob; - struct drm_mm *mm = man->priv; + struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; - spin_lock(&glob->lru_lock); - drm_mm_debug_table(mm, prefix); - spin_unlock(&glob->lru_lock); + spin_lock(&rman->lock); + drm_mm_debug_table(&rman->mm, prefix); + spin_unlock(&rman->lock); } const struct ttm_mem_type_manager_func ttm_bo_manager_func = { diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index a7bab87a548b..af789dc869b9 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -440,10 +440,8 @@ int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) return ret; ret = be->func->bind(be, bo_mem); - if (ret) { - printk(KERN_ERR TTM_PFX "Couldn't bind backend.\n"); + if (unlikely(ret != 0)) return ret; - } ttm->state = tt_bound; diff --git a/drivers/gpu/drm/via/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c index 9b5b4d9dd62c..3e038a394c51 100644 --- a/drivers/gpu/drm/via/via_dmablit.c +++ b/drivers/gpu/drm/via/via_dmablit.c @@ -235,9 +235,9 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) vsg->num_pages = VIA_PFN(xfer->mem_addr + (xfer->num_lines * xfer->mem_stride - 1)) - first_pfn + 1; - if (NULL == (vsg->pages = vmalloc(sizeof(struct page *) * vsg->num_pages))) + vsg->pages = vzalloc(sizeof(struct page *) * vsg->num_pages); + if (NULL == vsg->pages) return -ENOMEM; - memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages); down_read(¤t->mm->mmap_sem); ret = get_user_pages(current, current->mm, (unsigned long)xfer->mem_addr, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 51d9f9f1d7f2..76954e3528c1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -691,6 +691,7 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, fence_rep.error = ret; fence_rep.fence_seq = (uint64_t) sequence; + fence_rep.pad64 = 0; user_fence_rep = (struct drm_vmw_fence_rep __user *) (unsigned long)arg->fence_rep; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 87c6e6156d7d..cceeb42789b6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -720,6 +720,8 @@ static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb) &vmw_vram_ne_placement, false, &vmw_dmabuf_bo_free); vmw_overlay_resume_all(dev_priv); + if (unlikely(ret != 0)) + vfbs->buffer = NULL; return ret; } @@ -730,6 +732,9 @@ static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb) struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(&vfb->base); + if (unlikely(vfbs->buffer == NULL)) + return 0; + bo = &vfbs->buffer->base; ttm_bo_unref(&bo); vfbs->buffer = NULL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index a01c47ddb5bc..29113c9b26a8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -557,7 +557,7 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) return -EINVAL; } - dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv)); + dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL); if (!dev_priv->ldu_priv) return -ENOMEM; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index df2036ed18d5..f1a52f9e7298 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -585,7 +585,7 @@ int vmw_overlay_init(struct vmw_private *dev_priv) return -ENOSYS; } - overlay = kmalloc(GFP_KERNEL, sizeof(*overlay)); + overlay = kmalloc(sizeof(*overlay), GFP_KERNEL); if (!overlay) return -ENOMEM; diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig index 742c423567cf..0e1edd7311ff 100644 --- a/drivers/gpu/stub/Kconfig +++ b/drivers/gpu/stub/Kconfig @@ -3,6 +3,9 @@ config STUB_POULSBO depends on PCI # Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick + select VIDEO_OUTPUT_CONTROL if ACPI + select BACKLIGHT_CLASS_DEVICE if ACPI + select INPUT if ACPI select ACPI_VIDEO if ACPI help Choose this option if you have a system that has Intel GMA500 diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index 1e4c21fc1a89..86d822aa9bbf 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -178,11 +178,13 @@ static int ad7414_probe(struct i2c_client *client, { struct ad7414_data *data; int conf; - int err = 0; + int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_READ_WORD_DATA)) + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + err = -EOPNOTSUPP; goto exit; + } data = kzalloc(sizeof(struct ad7414_data), GFP_KERNEL); if (!data) { diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 9e775717abb7..87d92a56a939 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1286,8 +1286,10 @@ static int adt7470_probe(struct i2c_client *client, init_completion(&data->auto_update_stop); data->auto_update = kthread_run(adt7470_update_thread, client, dev_name(data->hwmon_dev)); - if (IS_ERR(data->auto_update)) + if (IS_ERR(data->auto_update)) { + err = PTR_ERR(data->auto_update); goto exit_unregister; + } return 0; diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index aa701a183707..f141a1de519c 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -376,10 +376,6 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, } } - err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); - if (err) - goto err_free_gpio; - fan_data->num_ctrl = num_ctrl; fan_data->ctrl = ctrl; fan_data->num_speed = pdata->num_speed; @@ -391,6 +387,10 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, goto err_free_gpio; } + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + if (err) + goto err_free_gpio; + return 0; err_free_gpio: diff --git a/drivers/input/input.c b/drivers/input/input.c index d092ef9291da..7f26ca6ecf75 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -74,6 +74,7 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz) * dev->event_lock held and interrupts disabled. */ static void input_pass_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int type, unsigned int code, int value) { struct input_handler *handler; @@ -92,6 +93,15 @@ static void input_pass_event(struct input_dev *dev, continue; handler = handle->handler; + + /* + * If this is the handler that injected this + * particular event we want to skip it to avoid + * filters firing again and again. + */ + if (handler == src_handler) + continue; + if (!handler->filter) { if (filtered) break; @@ -121,7 +131,7 @@ static void input_repeat_key(unsigned long data) if (test_bit(dev->repeat_key, dev->key) && is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { - input_pass_event(dev, EV_KEY, dev->repeat_key, 2); + input_pass_event(dev, NULL, EV_KEY, dev->repeat_key, 2); if (dev->sync) { /* @@ -130,7 +140,7 @@ static void input_repeat_key(unsigned long data) * Otherwise assume that the driver will send * SYN_REPORT once it's done. */ - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } if (dev->rep[REP_PERIOD]) @@ -163,6 +173,7 @@ static void input_stop_autorepeat(struct input_dev *dev) #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) static int input_handle_abs_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int code, int *pval) { bool is_mt_event; @@ -206,13 +217,15 @@ static int input_handle_abs_event(struct input_dev *dev, /* Flush pending "slot" event */ if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { input_abs_set_val(dev, ABS_MT_SLOT, dev->slot); - input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); + input_pass_event(dev, src_handler, + EV_ABS, ABS_MT_SLOT, dev->slot); } return INPUT_PASS_TO_HANDLERS; } static void input_handle_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int type, unsigned int code, int value) { int disposition = INPUT_IGNORE_EVENT; @@ -265,7 +278,8 @@ static void input_handle_event(struct input_dev *dev, case EV_ABS: if (is_event_supported(code, dev->absbit, ABS_MAX)) - disposition = input_handle_abs_event(dev, code, &value); + disposition = input_handle_abs_event(dev, src_handler, + code, &value); break; @@ -323,7 +337,7 @@ static void input_handle_event(struct input_dev *dev, dev->event(dev, type, code, value); if (disposition & INPUT_PASS_TO_HANDLERS) - input_pass_event(dev, type, code, value); + input_pass_event(dev, src_handler, type, code, value); } /** @@ -352,7 +366,7 @@ void input_event(struct input_dev *dev, spin_lock_irqsave(&dev->event_lock, flags); add_input_randomness(type, code, value); - input_handle_event(dev, type, code, value); + input_handle_event(dev, NULL, type, code, value); spin_unlock_irqrestore(&dev->event_lock, flags); } } @@ -382,7 +396,8 @@ void input_inject_event(struct input_handle *handle, rcu_read_lock(); grab = rcu_dereference(dev->grab); if (!grab || grab == handle) - input_handle_event(dev, type, code, value); + input_handle_event(dev, handle->handler, + type, code, value); rcu_read_unlock(); spin_unlock_irqrestore(&dev->event_lock, flags); @@ -595,10 +610,10 @@ static void input_dev_release_keys(struct input_dev *dev) for (code = 0; code <= KEY_MAX; code++) { if (is_event_supported(code, dev->keybit, KEY_MAX) && __test_and_clear_bit(code, dev->key)) { - input_pass_event(dev, EV_KEY, code, 0); + input_pass_event(dev, NULL, EV_KEY, code, 0); } } - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } } @@ -873,9 +888,9 @@ int input_set_keycode(struct input_dev *dev, !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __test_and_clear_bit(old_keycode, dev->key)) { - input_pass_event(dev, EV_KEY, old_keycode, 0); + input_pass_event(dev, NULL, EV_KEY, old_keycode, 0); if (dev->sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } out: @@ -1565,8 +1580,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) } \ } while (0) -#ifdef CONFIG_PM -static void input_dev_reset(struct input_dev *dev, bool activate) +static void input_dev_toggle(struct input_dev *dev, bool activate) { if (!dev->event) return; @@ -1580,12 +1594,44 @@ static void input_dev_reset(struct input_dev *dev, bool activate) } } +/** + * input_reset_device() - reset/restore the state of input device + * @dev: input device whose state needs to be reset + * + * This function tries to reset the state of an opened input device and + * bring internal state and state if the hardware in sync with each other. + * We mark all keys as released, restore LED state, repeat rate, etc. + */ +void input_reset_device(struct input_dev *dev) +{ + mutex_lock(&dev->mutex); + + if (dev->users) { + input_dev_toggle(dev, true); + + /* + * Keys that have been pressed at suspend time are unlikely + * to be still pressed when we resume. + */ + spin_lock_irq(&dev->event_lock); + input_dev_release_keys(dev); + spin_unlock_irq(&dev->event_lock); + } + + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL(input_reset_device); + +#ifdef CONFIG_PM static int input_dev_suspend(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); mutex_lock(&input_dev->mutex); - input_dev_reset(input_dev, false); + + if (input_dev->users) + input_dev_toggle(input_dev, false); + mutex_unlock(&input_dev->mutex); return 0; @@ -1595,18 +1641,7 @@ static int input_dev_resume(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); - mutex_lock(&input_dev->mutex); - input_dev_reset(input_dev, true); - - /* - * Keys that have been pressed at suspend time are unlikely - * to be still pressed when we resume. - */ - spin_lock_irq(&input_dev->event_lock); - input_dev_release_keys(input_dev); - spin_unlock_irq(&input_dev->event_lock); - - mutex_unlock(&input_dev->mutex); + input_reset_device(input_dev); return 0; } diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index b92d1cd5cba1..af45d275f686 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -4,7 +4,7 @@ * I2C QWERTY Keypad and IO Expander * Bugs: Enter bugs at http://blackfin.uclinux.org/ * - * Copyright (C) 2008-2009 Analog Devices Inc. + * Copyright (C) 2008-2010 Analog Devices Inc. * Licensed under the GPL-2 or later. */ @@ -24,29 +24,6 @@ #include <linux/i2c/adp5588.h> - /* Configuration Register1 */ -#define AUTO_INC (1 << 7) -#define GPIEM_CFG (1 << 6) -#define OVR_FLOW_M (1 << 5) -#define INT_CFG (1 << 4) -#define OVR_FLOW_IEN (1 << 3) -#define K_LCK_IM (1 << 2) -#define GPI_IEN (1 << 1) -#define KE_IEN (1 << 0) - -/* Interrupt Status Register */ -#define CMP2_INT (1 << 5) -#define CMP1_INT (1 << 4) -#define OVR_FLOW_INT (1 << 3) -#define K_LCK_INT (1 << 2) -#define GPI_INT (1 << 1) -#define KE_INT (1 << 0) - -/* Key Lock and Event Counter Register */ -#define K_LCK_EN (1 << 6) -#define LCK21 0x30 -#define KEC 0xF - /* Key Event Register xy */ #define KEY_EV_PRESSED (1 << 7) #define KEY_EV_MASK (0x7F) @@ -55,10 +32,6 @@ #define KEYP_MAX_EVENT 10 -#define MAXGPIO 18 -#define ADP_BANK(offs) ((offs) >> 3) -#define ADP_BIT(offs) (1u << ((offs) & 0x7)) - /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -75,7 +48,7 @@ struct adp5588_kpad { const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; #ifdef CONFIG_GPIOLIB - unsigned char gpiomap[MAXGPIO]; + unsigned char gpiomap[ADP5588_MAXGPIO]; bool export_gpio; struct gpio_chip gc; struct mutex gpio_lock; /* Protect cached dir, dat_out */ @@ -103,8 +76,8 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); } @@ -113,8 +86,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, unsigned off, int val) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); mutex_lock(&kpad->gpio_lock); @@ -132,8 +105,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; mutex_lock(&kpad->gpio_lock); @@ -150,8 +123,8 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, unsigned off, int val) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; mutex_lock(&kpad->gpio_lock); @@ -176,7 +149,7 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, const struct adp5588_kpad_platform_data *pdata) { - bool pin_used[MAXGPIO]; + bool pin_used[ADP5588_MAXGPIO]; int n_unused = 0; int i; @@ -191,7 +164,7 @@ static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, for (i = 0; i < kpad->gpimapsize; i++) pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; - for (i = 0; i < MAXGPIO; i++) + for (i = 0; i < ADP5588_MAXGPIO; i++) if (!pin_used[i]) kpad->gpiomap[n_unused++] = i; @@ -234,7 +207,7 @@ static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad) return error; } - for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { kpad->dat_out[i] = adp5588_read(kpad->client, GPIO_DAT_OUT1 + i); kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); @@ -318,11 +291,11 @@ static void adp5588_work(struct work_struct *work) status = adp5588_read(client, INT_STAT); - if (status & OVR_FLOW_INT) /* Unlikely and should never happen */ + if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ dev_err(&client->dev, "Event Overflow Error\n"); - if (status & KE_INT) { - ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; + if (status & ADP5588_KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC; if (ev_cnt) { adp5588_report_events(kpad, ev_cnt); input_sync(kpad->input); @@ -360,7 +333,7 @@ static int __devinit adp5588_setup(struct i2c_client *client) if (pdata->en_keylock) { ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1); ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2); - ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN); + ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN); } for (i = 0; i < KEYP_MAX_EVENT; i++) @@ -384,7 +357,7 @@ static int __devinit adp5588_setup(struct i2c_client *client) } if (gpio_data) { - for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { int pull_mask = gpio_data->pullup_dis_mask; ret |= adp5588_write(client, GPIO_PULL1 + i, @@ -392,11 +365,14 @@ static int __devinit adp5588_setup(struct i2c_client *client) } } - ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | - OVR_FLOW_INT | K_LCK_INT | - GPI_INT | KE_INT); /* Status is W1C */ + ret |= adp5588_write(client, INT_STAT, + ADP5588_CMP2_INT | ADP5588_CMP1_INT | + ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT | + ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */ - ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN); + ret |= adp5588_write(client, CFG, ADP5588_INT_CFG | + ADP5588_OVR_FLOW_IEN | + ADP5588_KE_IEN); if (ret < 0) { dev_err(&client->dev, "Write Error\n"); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index d358ef8623f4..11478eb2c27d 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -63,6 +63,10 @@ static bool atkbd_extra; module_param_named(extra, atkbd_extra, bool, 0); MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); +static bool atkbd_terminal; +module_param_named(terminal, atkbd_terminal, bool, 0); +MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); + /* * Scancode to keycode tables. These are just the default setting, and * are loadable via a userland utility. @@ -136,7 +140,8 @@ static const unsigned short atkbd_unxlate_table[128] = { #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ #define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ -#define ATKBD_CMD_SETALL_MBR 0x00fa +#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */ +#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */ #define ATKBD_CMD_RESET_BAT 0x02ff #define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_EX_ENABLE 0x10ea @@ -764,6 +769,11 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra } } + if (atkbd_terminal) { + ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB); + return 3; + } + if (target_set != 3) return 2; diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 4b42ffc0532a..d1583aea1721 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -127,14 +127,6 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 idev->id.product = 0x0001; idev->id.version = 0x0100; - input_set_drvdata(idev, lp); - - ret = input_register_device(idev); - if (ret) { - dev_err(&client->dev, "input_register_device() failed\n"); - goto fail_register; - } - lp->laststate = read_state(lp); ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler, @@ -142,16 +134,21 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 DRV_NAME, lp); if (ret) { dev_err(&client->dev, "IRQ %d is not free\n", client->irq); - goto fail_irq; + goto fail_free_device; + } + + ret = input_register_device(idev); + if (ret) { + dev_err(&client->dev, "input_register_device() failed\n"); + goto fail_free_irq; } i2c_set_clientdata(client, lp); return 0; - fail_irq: - input_unregister_device(idev); - fail_register: - input_set_drvdata(idev, NULL); + fail_free_irq: + free_irq(client->irq, lp); + fail_free_device: input_free_device(idev); fail_allocate: kfree(lp); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index ed7ad7416b24..a5475b577086 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -351,6 +351,17 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { }, }, { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + }, + { /* Amoi M636/A737 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c index aea9a9399a36..d94f7e9aa997 100644 --- a/drivers/input/tablet/acecad.c +++ b/drivers/input/tablet/acecad.c @@ -229,12 +229,13 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ err = input_register_device(acecad->input); if (err) - goto fail2; + goto fail3; usb_set_intfdata(intf, acecad); return 0; + fail3: usb_free_urb(acecad->irq); fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); fail1: input_free_device(input_dev); kfree(acecad); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index cc2a88d5192f..77b8fd20cd90 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -10,7 +10,7 @@ menuconfig NEW_LEDS if NEW_LEDS config LEDS_CLASS - tristate "LED Class Support" + bool "LED Class Support" help This option enables the led sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. @@ -176,6 +176,24 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP5521 + tristate "LED Support for N.S. LP5521 LED driver chip" + depends on LEDS_CLASS && I2C + help + If you say yes here you get support for the National Semiconductor + LP5521 LED driver. It is 3 channel chip with programmable engines. + Driver provides direct control via LED class and interface for + programming the engines. + +config LEDS_LP5523 + tristate "LED Support for N.S. LP5523 LED driver chip" + depends on LEDS_CLASS && I2C + help + If you say yes here you get support for the National Semiconductor + LP5523 LED driver. It is 9 channel chip with programmable engines. + Driver provides direct control via LED class and interface for + programming the engines. + config LEDS_CLEVO_MAIL tristate "Mail LED on Clevo notebook" depends on X86 && SERIO_I8042 && DMI diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9c96db40ef6d..aae6989ff6b6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -23,6 +23,8 @@ obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o +obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 260660076507..211e21f34bd5 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -81,6 +81,79 @@ static struct device_attribute led_class_attrs[] = { __ATTR_NULL, }; +static void led_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (void *)data; + unsigned long brightness; + unsigned long delay; + + if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + + brightness = led_get_brightness(led_cdev); + if (!brightness) { + /* Time to switch the LED on. */ + brightness = led_cdev->blink_brightness; + delay = led_cdev->blink_delay_on; + } else { + /* Store the current brightness value to be able + * to restore it when the delay_off period is over. + */ + led_cdev->blink_brightness = brightness; + brightness = LED_OFF; + delay = led_cdev->blink_delay_off; + } + + led_set_brightness(led_cdev, brightness); + + mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); +} + +static void led_stop_software_blink(struct led_classdev *led_cdev) +{ + /* deactivate previous settings */ + del_timer_sync(&led_cdev->blink_timer); + led_cdev->blink_delay_on = 0; + led_cdev->blink_delay_off = 0; +} + +static void led_set_software_blink(struct led_classdev *led_cdev, + unsigned long delay_on, + unsigned long delay_off) +{ + int current_brightness; + + current_brightness = led_get_brightness(led_cdev); + if (current_brightness) + led_cdev->blink_brightness = current_brightness; + if (!led_cdev->blink_brightness) + led_cdev->blink_brightness = led_cdev->max_brightness; + + if (delay_on == led_cdev->blink_delay_on && + delay_off == led_cdev->blink_delay_off) + return; + + led_stop_software_blink(led_cdev); + + led_cdev->blink_delay_on = delay_on; + led_cdev->blink_delay_off = delay_off; + + /* never on - don't blink */ + if (!delay_on) + return; + + /* never off - just set to brightness */ + if (!delay_off) { + led_set_brightness(led_cdev, led_cdev->blink_brightness); + return; + } + + mod_timer(&led_cdev->blink_timer, jiffies + 1); +} + + /** * led_classdev_suspend - suspend an led_classdev. * @led_cdev: the led_classdev to suspend. @@ -148,6 +221,10 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_update_brightness(led_cdev); + init_timer(&led_cdev->blink_timer); + led_cdev->blink_timer.function = led_timer_function; + led_cdev->blink_timer.data = (unsigned long)led_cdev; + #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); #endif @@ -157,7 +234,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) return 0; } - EXPORT_SYMBOL_GPL(led_classdev_register); /** @@ -175,6 +251,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev) up_write(&led_cdev->trigger_lock); #endif + /* Stop blinking */ + led_brightness_set(led_cdev, LED_OFF); + device_unregister(led_cdev->dev); down_write(&leds_list_lock); @@ -183,6 +262,30 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); +void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + if (led_cdev->blink_set && + led_cdev->blink_set(led_cdev, delay_on, delay_off)) + return; + + /* blink with 1 Hz as default if nothing specified */ + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + led_set_software_blink(led_cdev, *delay_on, *delay_off); +} +EXPORT_SYMBOL(led_blink_set); + +void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + led_stop_software_blink(led_cdev); + led_cdev->brightness_set(led_cdev, brightness); +} +EXPORT_SYMBOL(led_brightness_set); + static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index f1c00db88b5e..c41eb6180c9c 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -113,7 +113,7 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); led_cdev->trigger = NULL; - led_set_brightness(led_cdev, LED_OFF); + led_brightness_set(led_cdev, LED_OFF); } if (trigger) { write_lock_irqsave(&trigger->leddev_list_lock, flags); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index ea57e05d08f3..4d9fa38d9ff6 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -316,7 +316,7 @@ static struct of_platform_driver of_gpio_leds_driver = { static int __init gpio_led_init(void) { - int ret; + int ret = 0; #ifdef CONFIG_LEDS_GPIO_PLATFORM ret = platform_driver_register(&gpio_led_driver); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c new file mode 100644 index 000000000000..3782f31f06d2 --- /dev/null +++ b/drivers/leds/leds-lp5521.c @@ -0,0 +1,821 @@ +/* + * LP5521 LED chip driver. + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/leds.h> +#include <linux/leds-lp5521.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define LP5521_PROGRAM_LENGTH 32 /* in bytes */ + +#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */ +#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */ + +#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */ +#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */ + +#define LP5521_CMD_LOAD 0x15 /* 00010101 */ +#define LP5521_CMD_RUN 0x2a /* 00101010 */ +#define LP5521_CMD_DIRECT 0x3f /* 00111111 */ +#define LP5521_CMD_DISABLED 0x00 /* 00000000 */ + +/* Registers */ +#define LP5521_REG_ENABLE 0x00 +#define LP5521_REG_OP_MODE 0x01 +#define LP5521_REG_R_PWM 0x02 +#define LP5521_REG_G_PWM 0x03 +#define LP5521_REG_B_PWM 0x04 +#define LP5521_REG_R_CURRENT 0x05 +#define LP5521_REG_G_CURRENT 0x06 +#define LP5521_REG_B_CURRENT 0x07 +#define LP5521_REG_CONFIG 0x08 +#define LP5521_REG_R_CHANNEL_PC 0x09 +#define LP5521_REG_G_CHANNEL_PC 0x0A +#define LP5521_REG_B_CHANNEL_PC 0x0B +#define LP5521_REG_STATUS 0x0C +#define LP5521_REG_RESET 0x0D +#define LP5521_REG_GPO 0x0E +#define LP5521_REG_R_PROG_MEM 0x10 +#define LP5521_REG_G_PROG_MEM 0x30 +#define LP5521_REG_B_PROG_MEM 0x50 + +#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM +#define LP5521_PROG_MEM_SIZE 0x20 + +/* Base register to set LED current */ +#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT + +/* Base register to set the brightness */ +#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM + +/* Bits in ENABLE register */ +#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ +#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ +#define LP5521_EXEC_RUN 0x2A + +/* Bits in CONFIG register */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + +/* Status */ +#define LP5521_EXT_CLK_USED 0x08 + +struct lp5521_engine { + const struct attribute_group *attributes; + int id; + u8 mode; + u8 prog_page; + u8 engine_mask; +}; + +struct lp5521_led { + int id; + u8 chan_nr; + u8 led_current; + u8 max_current; + struct led_classdev cdev; + struct work_struct brightness_work; + u8 brightness; +}; + +struct lp5521_chip { + struct lp5521_platform_data *pdata; + struct mutex lock; /* Serialize control */ + struct i2c_client *client; + struct lp5521_engine engines[LP5521_MAX_ENGINES]; + struct lp5521_led leds[LP5521_MAX_LEDS]; + u8 num_channels; + u8 num_leds; +}; + +#define cdev_to_led(c) container_of(c, struct lp5521_led, cdev) +#define engine_to_lp5521(eng) container_of((eng), struct lp5521_chip, \ + engines[(eng)->id - 1]) +#define led_to_lp5521(led) container_of((led), struct lp5521_chip, \ + leds[(led)->id]) + +static void lp5521_led_brightness_work(struct work_struct *work); + +static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return -EIO; + + *buf = ret; + return 0; +} + +static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + int ret; + u8 engine_state; + + /* Only transition between RUN and DIRECT mode are handled here */ + if (mode == LP5521_CMD_LOAD) + return 0; + + if (mode == LP5521_CMD_DISABLED) + mode = LP5521_CMD_DIRECT; + + ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); + + /* set mode only for this engine */ + engine_state &= ~(engine->engine_mask); + mode &= engine->engine_mask; + engine_state |= mode; + ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state); + + return ret; +} + +static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) +{ + struct lp5521_chip *chip = engine_to_lp5521(eng); + struct i2c_client *client = chip->client; + int ret; + int addr; + u8 mode; + + /* move current engine to direct mode and remember the state */ + ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + usleep_range(1000, 10000); + ret |= lp5521_read(client, LP5521_REG_OP_MODE, &mode); + + /* For loading, all the engines to load mode */ + lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + usleep_range(1000, 10000); + lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); + usleep_range(1000, 10000); + + addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE; + i2c_smbus_write_i2c_block_data(client, + addr, + LP5521_PROG_MEM_SIZE, + pattern); + + ret |= lp5521_write(client, LP5521_REG_OP_MODE, mode); + return ret; +} + +static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) +{ + return lp5521_write(chip->client, + LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + curr); +} + +static void lp5521_init_engine(struct lp5521_chip *chip, + const struct attribute_group *attr_group) +{ + int i; + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + chip->engines[i].id = i + 1; + chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); + chip->engines[i].prog_page = i; + chip->engines[i].attributes = &attr_group[i]; + } +} + +static int lp5521_configure(struct i2c_client *client, + const struct attribute_group *attr_group) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + int ret; + + lp5521_init_engine(chip, attr_group); + + lp5521_write(client, LP5521_REG_RESET, 0xff); + + usleep_range(10000, 20000); + + /* Set all PWMs to direct control mode */ + ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); + + /* Enable auto-powersave, set charge pump to auto, red to battery */ + ret |= lp5521_write(client, LP5521_REG_CONFIG, + LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + + /* Initialize all channels PWM to zero -> leds off */ + ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); + ret |= lp5521_write(client, LP5521_REG_G_PWM, 0); + ret |= lp5521_write(client, LP5521_REG_B_PWM, 0); + + /* Set engines are set to run state when OP_MODE enables engines */ + ret |= lp5521_write(client, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | + LP5521_EXEC_RUN); + /* enable takes 500us */ + usleep_range(500, 20000); + + return ret; +} + +static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) +{ + int ret; + u8 status; + + ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); + if (ret < 0) + return ret; + + /* Check that ext clock is really in use if requested */ + if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT) + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; + return 0; +} + +static void lp5521_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lp5521_led *led = cdev_to_led(cdev); + led->brightness = (u8)brightness; + schedule_work(&led->brightness_work); +} + +static void lp5521_led_brightness_work(struct work_struct *work) +{ + struct lp5521_led *led = container_of(work, + struct lp5521_led, + brightness_work); + struct lp5521_chip *chip = led_to_lp5521(led); + struct i2c_client *client = chip->client; + + mutex_lock(&chip->lock); + lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + mutex_unlock(&chip->lock); +} + +/* Detect the chip by setting its ENABLE register and reading it back. */ +static int lp5521_detect(struct i2c_client *client) +{ + int ret; + u8 buf; + + ret = lp5521_write(client, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); + if (ret) + return ret; + usleep_range(1000, 10000); + ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); + if (ret) + return ret; + if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)) + return -ENODEV; + + return 0; +} + +/* Set engine mode and create appropriate sysfs attributes, if required. */ +static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + struct device *dev = &client->dev; + int ret = 0; + + /* if in that mode already do nothing, except for run */ + if (mode == engine->mode && mode != LP5521_CMD_RUN) + return 0; + + if (mode == LP5521_CMD_RUN) { + ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN); + } else if (mode == LP5521_CMD_LOAD) { + lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); + lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); + + ret = sysfs_create_group(&dev->kobj, engine->attributes); + if (ret) + return ret; + } else if (mode == LP5521_CMD_DISABLED) { + lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); + } + + /* remove load attribute from sysfs if not in load mode */ + if (engine->mode == LP5521_CMD_LOAD && mode != LP5521_CMD_LOAD) + sysfs_remove_group(&dev->kobj, engine->attributes); + + engine->mode = mode; + + return ret; +} + +static int lp5521_do_store_load(struct lp5521_engine *engine, + const char *buf, size_t len) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + int ret, nrchars, offset = 0, i = 0; + char c[3]; + unsigned cmd; + u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; + + while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto fail; + pattern[i] = (u8)cmd; + + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto fail; + + mutex_lock(&chip->lock); + ret = lp5521_load_program(engine, pattern); + mutex_unlock(&chip->lock); + + if (ret) { + dev_err(&client->dev, "failed loading pattern\n"); + return ret; + } + + return len; +fail: + dev_err(&client->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static ssize_t store_engine_load(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + return lp5521_do_store_load(&chip->engines[nr - 1], buf, len); +} + +#define store_load(nr) \ +static ssize_t store_engine##nr##_load(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_load(dev, attr, buf, len, nr); \ +} +store_load(1) +store_load(2) +store_load(3) + +static ssize_t show_engine_mode(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + switch (chip->engines[nr - 1].mode) { + case LP5521_CMD_RUN: + return sprintf(buf, "run\n"); + case LP5521_CMD_LOAD: + return sprintf(buf, "load\n"); + case LP5521_CMD_DISABLED: + return sprintf(buf, "disabled\n"); + default: + return sprintf(buf, "disabled\n"); + } +} + +#define show_mode(nr) \ +static ssize_t show_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_mode(dev, attr, buf, nr); \ +} +show_mode(1) +show_mode(2) +show_mode(3) + +static ssize_t store_engine_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp5521_engine *engine = &chip->engines[nr - 1]; + mutex_lock(&chip->lock); + + if (!strncmp(buf, "run", 3)) + lp5521_set_mode(engine, LP5521_CMD_RUN); + else if (!strncmp(buf, "load", 4)) + lp5521_set_mode(engine, LP5521_CMD_LOAD); + else if (!strncmp(buf, "disabled", 8)) + lp5521_set_mode(engine, LP5521_CMD_DISABLED); + + mutex_unlock(&chip->lock); + return len; +} + +#define store_mode(nr) \ +static ssize_t store_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_mode(dev, attr, buf, len, nr); \ +} +store_mode(1) +store_mode(2) +store_mode(3) + +static ssize_t show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static ssize_t show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + struct lp5521_chip *chip = led_to_lp5521(led); + ssize_t ret; + unsigned long curr; + + if (strict_strtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + mutex_lock(&chip->lock); + ret = lp5521_set_led_current(chip, led->id, curr); + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + led->led_current = (u8)curr; + + return len; +} + +static ssize_t lp5521_selftest(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + int ret; + + mutex_lock(&chip->lock); + ret = lp5521_run_selftest(chip, buf); + mutex_unlock(&chip->lock); + return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); +} + +/* led class device attributes */ +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); + +static struct attribute *lp5521_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, + NULL, +}; + +static struct attribute_group lp5521_led_attribute_group = { + .attrs = lp5521_led_attributes +}; + +/* device attributes */ +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, + show_engine1_mode, store_engine1_mode); +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, + show_engine2_mode, store_engine2_mode); +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, + show_engine3_mode, store_engine3_mode); +static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); + +static struct attribute *lp5521_attributes[] = { + &dev_attr_engine1_mode.attr, + &dev_attr_engine2_mode.attr, + &dev_attr_engine3_mode.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute *lp5521_engine1_attributes[] = { + &dev_attr_engine1_load.attr, + NULL +}; + +static struct attribute *lp5521_engine2_attributes[] = { + &dev_attr_engine2_load.attr, + NULL +}; + +static struct attribute *lp5521_engine3_attributes[] = { + &dev_attr_engine3_load.attr, + NULL +}; + +static const struct attribute_group lp5521_group = { + .attrs = lp5521_attributes, +}; + +static const struct attribute_group lp5521_engine_group[] = { + {.attrs = lp5521_engine1_attributes }, + {.attrs = lp5521_engine2_attributes }, + {.attrs = lp5521_engine3_attributes }, +}; + +static int lp5521_register_sysfs(struct i2c_client *client) +{ + struct device *dev = &client->dev; + return sysfs_create_group(&dev->kobj, &lp5521_group); +} + +static void lp5521_unregister_sysfs(struct i2c_client *client) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int i; + + sysfs_remove_group(&dev->kobj, &lp5521_group); + + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + if (chip->engines[i].mode == LP5521_CMD_LOAD) + sysfs_remove_group(&dev->kobj, + chip->engines[i].attributes); + } + + for (i = 0; i < chip->num_leds; i++) + sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, + &lp5521_led_attribute_group); +} + +static int __init lp5521_init_led(struct lp5521_led *led, + struct i2c_client *client, + int chan, struct lp5521_platform_data *pdata) +{ + struct device *dev = &client->dev; + char name[32]; + int res; + + if (chan >= LP5521_MAX_LEDS) + return -EINVAL; + + if (pdata->led_config[chan].led_current == 0) + return 0; + + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= LP5521_MAX_LEDS) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + LP5521_MAX_LEDS - 1); + return -EINVAL; + } + + snprintf(name, sizeof(name), "%s:channel%d", client->name, chan); + led->cdev.brightness_set = lp5521_set_brightness; + led->cdev.name = name; + res = led_classdev_register(dev, &led->cdev); + if (res < 0) { + dev_err(dev, "couldn't register led on channel %d\n", chan); + return res; + } + + res = sysfs_create_group(&led->cdev.dev->kobj, + &lp5521_led_attribute_group); + if (res < 0) { + dev_err(dev, "couldn't register current attribute\n"); + led_classdev_unregister(&led->cdev); + return res; + } + return 0; +} + +static int lp5521_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lp5521_chip *chip; + struct lp5521_platform_data *pdata; + int ret, i, led; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto fail1; + } + + mutex_init(&chip->lock); + + chip->pdata = pdata; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + goto fail1; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 10000); + pdata->enable(1); + usleep_range(1000, 10000); /* Spec says min 500us */ + } + + ret = lp5521_detect(client); + + if (ret) { + dev_err(&client->dev, "Chip not found\n"); + goto fail2; + } + + dev_info(&client->dev, "%s programmable led chip found\n", id->name); + + ret = lp5521_configure(client, lp5521_engine_group); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto fail2; + } + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5521_init_led(&chip->leds[led], client, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + goto fail3; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set initial LED current */ + lp5521_set_led_current(chip, led, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5521_led_brightness_work); + + led++; + } + + ret = lp5521_register_sysfs(client); + if (ret) { + dev_err(&client->dev, "registering sysfs failed\n"); + goto fail3; + } + return ret; +fail3: + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +fail2: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +fail1: + kfree(chip); + return ret; +} + +static int lp5521_remove(struct i2c_client *client) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + int i; + + lp5521_unregister_sysfs(client); + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } + + if (chip->pdata->enable) + chip->pdata->enable(0); + if (chip->pdata->release_resources) + chip->pdata->release_resources(); + kfree(chip); + return 0; +} + +static const struct i2c_device_id lp5521_id[] = { + { "lp5521", 0 }, /* Three channel chip */ + { } +}; +MODULE_DEVICE_TABLE(i2c, lp5521_id); + +static struct i2c_driver lp5521_driver = { + .driver = { + .name = "lp5521", + }, + .probe = lp5521_probe, + .remove = lp5521_remove, + .id_table = lp5521_id, +}; + +static int __init lp5521_init(void) +{ + int ret; + + ret = i2c_add_driver(&lp5521_driver); + + if (ret < 0) + printk(KERN_ALERT "Adding lp5521 driver failed\n"); + + return ret; +} + +static void __exit lp5521_exit(void) +{ + i2c_del_driver(&lp5521_driver); +} + +module_init(lp5521_init); +module_exit(lp5521_exit); + +MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_DESCRIPTION("LP5521 LED engine"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c new file mode 100644 index 000000000000..1e11fcc08b28 --- /dev/null +++ b/drivers/leds/leds-lp5523.c @@ -0,0 +1,1065 @@ +/* + * lp5523.c - LP5523 LED Driver + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/leds.h> +#include <linux/leds-lp5523.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define LP5523_REG_ENABLE 0x00 +#define LP5523_REG_OP_MODE 0x01 +#define LP5523_REG_RATIOMETRIC_MSB 0x02 +#define LP5523_REG_RATIOMETRIC_LSB 0x03 +#define LP5523_REG_ENABLE_LEDS_MSB 0x04 +#define LP5523_REG_ENABLE_LEDS_LSB 0x05 +#define LP5523_REG_LED_CNTRL_BASE 0x06 +#define LP5523_REG_LED_PWM_BASE 0x16 +#define LP5523_REG_LED_CURRENT_BASE 0x26 +#define LP5523_REG_CONFIG 0x36 +#define LP5523_REG_CHANNEL1_PC 0x37 +#define LP5523_REG_CHANNEL2_PC 0x38 +#define LP5523_REG_CHANNEL3_PC 0x39 +#define LP5523_REG_STATUS 0x3a +#define LP5523_REG_GPO 0x3b +#define LP5523_REG_VARIABLE 0x3c +#define LP5523_REG_RESET 0x3d +#define LP5523_REG_TEMP_CTRL 0x3e +#define LP5523_REG_TEMP_READ 0x3f +#define LP5523_REG_TEMP_WRITE 0x40 +#define LP5523_REG_LED_TEST_CTRL 0x41 +#define LP5523_REG_LED_TEST_ADC 0x42 +#define LP5523_REG_ENG1_VARIABLE 0x45 +#define LP5523_REG_ENG2_VARIABLE 0x46 +#define LP5523_REG_ENG3_VARIABLE 0x47 +#define LP5523_REG_MASTER_FADER1 0x48 +#define LP5523_REG_MASTER_FADER2 0x49 +#define LP5523_REG_MASTER_FADER3 0x4a +#define LP5523_REG_CH1_PROG_START 0x4c +#define LP5523_REG_CH2_PROG_START 0x4d +#define LP5523_REG_CH3_PROG_START 0x4e +#define LP5523_REG_PROG_PAGE_SEL 0x4f +#define LP5523_REG_PROG_MEM 0x50 + +#define LP5523_CMD_LOAD 0x15 /* 00010101 */ +#define LP5523_CMD_RUN 0x2a /* 00101010 */ +#define LP5523_CMD_DISABLED 0x00 /* 00000000 */ + +#define LP5523_ENABLE 0x40 +#define LP5523_AUTO_INC 0x40 +#define LP5523_PWR_SAVE 0x20 +#define LP5523_PWM_PWR_SAVE 0x04 +#define LP5523_CP_1 0x08 +#define LP5523_CP_1_5 0x10 +#define LP5523_CP_AUTO 0x18 +#define LP5523_INT_CLK 0x01 +#define LP5523_AUTO_CLK 0x02 +#define LP5523_EN_LEDTEST 0x80 +#define LP5523_LEDTEST_DONE 0x80 + +#define LP5523_DEFAULT_CURRENT 50 /* microAmps */ +#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ +#define LP5523_PROGRAM_PAGES 6 +#define LP5523_ADC_SHORTCIRC_LIM 80 + +#define LP5523_LEDS 9 +#define LP5523_ENGINES 3 + +#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ + +#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */ + +#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING + +#define LP5523_EXT_CLK_USED 0x08 + +#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) +#define SHIFT_MASK(id) (((id) - 1) * 2) + +struct lp5523_engine { + const struct attribute_group *attributes; + int id; + u8 mode; + u8 prog_page; + u8 mux_page; + u16 led_mux; + u8 engine_mask; +}; + +struct lp5523_led { + int id; + u8 chan_nr; + u8 led_current; + u8 max_current; + struct led_classdev cdev; + struct work_struct brightness_work; + u8 brightness; +}; + +struct lp5523_chip { + struct mutex lock; /* Serialize control */ + struct i2c_client *client; + struct lp5523_engine engines[LP5523_ENGINES]; + struct lp5523_led leds[LP5523_LEDS]; + struct lp5523_platform_data *pdata; + u8 num_channels; + u8 num_leds; +}; + +#define cdev_to_led(c) container_of(c, struct lp5523_led, cdev) + +static struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) +{ + return container_of(engine, struct lp5523_chip, + engines[engine->id - 1]); +} + +static struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) +{ + return container_of(led, struct lp5523_chip, + leds[led->id]); +} + +static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode); +static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); +static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern); + +static void lp5523_led_brightness_work(struct work_struct *work); + +static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) +{ + s32 ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + return -EIO; + + *buf = ret; + return 0; +} + +static int lp5523_detect(struct i2c_client *client) +{ + int ret; + u8 buf; + + ret = lp5523_write(client, LP5523_REG_ENABLE, 0x40); + if (ret) + return ret; + ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); + if (ret) + return ret; + if (buf == 0x40) + return 0; + else + return -ENODEV; +} + +static int lp5523_configure(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + int ret = 0; + u8 status; + + /* one pattern per engine setting led mux start and stop addresses */ + u8 pattern[][LP5523_PROGRAM_LENGTH] = { + { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, + { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, + { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, + }; + + lp5523_write(client, LP5523_REG_RESET, 0xff); + + usleep_range(10000, 100000); + + ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + /* Chip startup time after reset is 500 us */ + usleep_range(1000, 10000); + + ret |= lp5523_write(client, LP5523_REG_CONFIG, + LP5523_AUTO_INC | LP5523_PWR_SAVE | + LP5523_CP_AUTO | LP5523_AUTO_CLK | + LP5523_PWM_PWR_SAVE); + + /* turn on all leds */ + ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); + + /* hardcode 32 bytes of memory for each engine from program memory */ + ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00); + ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10); + ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20); + + /* write led mux address space for each channel */ + ret |= lp5523_load_program(&chip->engines[0], pattern[0]); + ret |= lp5523_load_program(&chip->engines[1], pattern[1]); + ret |= lp5523_load_program(&chip->engines[2], pattern[2]); + + if (ret) { + dev_err(&client->dev, "could not load mux programs\n"); + return -1; + } + + /* set all engines exec state and mode to run 00101010 */ + ret |= lp5523_write(client, LP5523_REG_ENABLE, + (LP5523_CMD_RUN | LP5523_ENABLE)); + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN); + + if (ret) { + dev_err(&client->dev, "could not start mux programs\n"); + return -1; + } + + /* Wait 3ms and check the engine status */ + usleep_range(3000, 20000); + lp5523_read(client, LP5523_REG_STATUS, &status); + status &= LP5523_ENG_STATUS_MASK; + + if (status == LP5523_ENG_STATUS_MASK) { + dev_dbg(&client->dev, "all engines configured\n"); + } else { + dev_info(&client->dev, "status == %x\n", status); + dev_err(&client->dev, "cound not configure LED engine\n"); + return -1; + } + + dev_info(&client->dev, "disabling engines\n"); + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); + + return ret; +} + +static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret; + u8 engine_state; + + ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); + if (ret) + goto fail; + + engine_state &= ~(engine->engine_mask); + + /* set mode only for this engine */ + mode &= engine->engine_mask; + + engine_state |= mode; + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); +fail: + return ret; +} + +static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret = 0; + + ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page); + ret |= lp5523_write(client, LP5523_REG_PROG_MEM, + (u8)(mux >> 8)); + ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux)); + engine->led_mux = mux; + + return ret; +} + +static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + + int ret = 0; + + ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, + engine->prog_page); + ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM, + LP5523_PROGRAM_LENGTH, pattern); + + return ret; +} + +static int lp5523_run_program(struct lp5523_engine *engine) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret; + + ret = lp5523_write(client, LP5523_REG_ENABLE, + LP5523_CMD_RUN | LP5523_ENABLE); + if (ret) + goto fail; + + ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN); +fail: + return ret; +} + +static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) +{ + int i; + u16 tmp_mux = 0; + len = len < LP5523_LEDS ? len : LP5523_LEDS; + for (i = 0; i < len; i++) { + switch (buf[i]) { + case '1': + tmp_mux |= (1 << i); + break; + case '0': + break; + case '\n': + i = len; + break; + default: + return -1; + } + } + *mux = tmp_mux; + + return 0; +} + +static void lp5523_mux_to_array(u16 led_mux, char *array) +{ + int i, pos = 0; + for (i = 0; i < LP5523_LEDS; i++) + pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); + + array[pos] = '\0'; +} + +/*--------------------------------------------------------------*/ +/* Sysfs interface */ +/*--------------------------------------------------------------*/ + +static ssize_t show_engine_leds(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + char mux[LP5523_LEDS + 1]; + + lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); + + return sprintf(buf, "%s\n", mux); +} + +#define show_leds(nr) \ +static ssize_t show_engine##nr##_leds(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_leds(dev, attr, buf, nr); \ +} +show_leds(1) +show_leds(2) +show_leds(3) + +static ssize_t store_engine_leds(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + u16 mux = 0; + + if (lp5523_mux_parse(buf, &mux, len)) + return -EINVAL; + + if (lp5523_load_mux(&chip->engines[nr - 1], mux)) + return -EINVAL; + + return len; +} + +#define store_leds(nr) \ +static ssize_t store_engine##nr##_leds(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_leds(dev, attr, buf, len, nr); \ +} +store_leds(1) +store_leds(2) +store_leds(3) + +static ssize_t lp5523_selftest(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + int i, ret, pos = 0; + int led = 0; + u8 status, adc, vdd; + + mutex_lock(&chip->lock); + + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (ret < 0) + goto fail; + + /* Check that ext clock is really in use if requested */ + if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT)) + if ((status & LP5523_EXT_CLK_USED) == 0) + goto fail; + + /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ + lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, + LP5523_EN_LEDTEST | 16); + usleep_range(3000, 10000); + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (!(status & LP5523_LEDTEST_DONE)) + usleep_range(3000, 10000); + + ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); + vdd--; /* There may be some fluctuation in measurement */ + + for (i = 0; i < LP5523_LEDS; i++) { + /* Skip non-existing channels */ + if (chip->pdata->led_config[i].led_current == 0) + continue; + + /* Set default current */ + lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + i, + chip->pdata->led_config[i].led_current); + + lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); + /* let current stabilize 2ms before measurements start */ + usleep_range(2000, 10000); + lp5523_write(chip->client, + LP5523_REG_LED_TEST_CTRL, + LP5523_EN_LEDTEST | i); + /* ledtest takes 2.7ms */ + usleep_range(3000, 10000); + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (!(status & LP5523_LEDTEST_DONE)) + usleep_range(3000, 10000); + ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); + + if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) + pos += sprintf(buf + pos, "LED %d FAIL\n", i); + + lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00); + + /* Restore current */ + lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + i, + chip->leds[led].led_current); + led++; + } + if (pos == 0) + pos = sprintf(buf, "OK\n"); + goto release_lock; +fail: + pos = sprintf(buf, "FAIL\n"); + +release_lock: + mutex_unlock(&chip->lock); + + return pos; +} + +static void lp5523_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lp5523_led *led = cdev_to_led(cdev); + + led->brightness = (u8)brightness; + + schedule_work(&led->brightness_work); +} + +static void lp5523_led_brightness_work(struct work_struct *work) +{ + struct lp5523_led *led = container_of(work, + struct lp5523_led, + brightness_work); + struct lp5523_chip *chip = led_to_lp5523(led); + struct i2c_client *client = chip->client; + + mutex_lock(&chip->lock); + + lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + + mutex_unlock(&chip->lock); +} + +static int lp5523_do_store_load(struct lp5523_engine *engine, + const char *buf, size_t len) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret, nrchars, offset = 0, i = 0; + char c[3]; + unsigned cmd; + u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + + while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto fail; + pattern[i] = (u8)cmd; + + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto fail; + + mutex_lock(&chip->lock); + + ret = lp5523_load_program(engine, pattern); + mutex_unlock(&chip->lock); + + if (ret) { + dev_err(&client->dev, "failed loading pattern\n"); + return ret; + } + + return len; +fail: + dev_err(&client->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static ssize_t store_engine_load(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + return lp5523_do_store_load(&chip->engines[nr - 1], buf, len); +} + +#define store_load(nr) \ +static ssize_t store_engine##nr##_load(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_load(dev, attr, buf, len, nr); \ +} +store_load(1) +store_load(2) +store_load(3) + +static ssize_t show_engine_mode(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + switch (chip->engines[nr - 1].mode) { + case LP5523_CMD_RUN: + return sprintf(buf, "run\n"); + case LP5523_CMD_LOAD: + return sprintf(buf, "load\n"); + case LP5523_CMD_DISABLED: + return sprintf(buf, "disabled\n"); + default: + return sprintf(buf, "disabled\n"); + } +} + +#define show_mode(nr) \ +static ssize_t show_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_mode(dev, attr, buf, nr); \ +} +show_mode(1) +show_mode(2) +show_mode(3) + +static ssize_t store_engine_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp5523_engine *engine = &chip->engines[nr - 1]; + mutex_lock(&chip->lock); + + if (!strncmp(buf, "run", 3)) + lp5523_set_mode(engine, LP5523_CMD_RUN); + else if (!strncmp(buf, "load", 4)) + lp5523_set_mode(engine, LP5523_CMD_LOAD); + else if (!strncmp(buf, "disabled", 8)) + lp5523_set_mode(engine, LP5523_CMD_DISABLED); + + mutex_unlock(&chip->lock); + return len; +} + +#define store_mode(nr) \ +static ssize_t store_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_mode(dev, attr, buf, len, nr); \ +} +store_mode(1) +store_mode(2) +store_mode(3) + +static ssize_t show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static ssize_t show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + struct lp5523_chip *chip = led_to_lp5523(led); + ssize_t ret; + unsigned long curr; + + if (strict_strtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + mutex_lock(&chip->lock); + ret = lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + (u8)curr); + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + led->led_current = (u8)curr; + + return len; +} + +/* led class device attributes */ +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); + +static struct attribute *lp5523_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, + NULL, +}; + +static struct attribute_group lp5523_led_attribute_group = { + .attrs = lp5523_led_attributes +}; + +/* device attributes */ +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, + show_engine1_mode, store_engine1_mode); +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, + show_engine2_mode, store_engine2_mode); +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, + show_engine3_mode, store_engine3_mode); +static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO, + show_engine1_leds, store_engine1_leds); +static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO, + show_engine2_leds, store_engine2_leds); +static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO, + show_engine3_leds, store_engine3_leds); +static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); + +static struct attribute *lp5523_attributes[] = { + &dev_attr_engine1_mode.attr, + &dev_attr_engine2_mode.attr, + &dev_attr_engine3_mode.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute *lp5523_engine1_attributes[] = { + &dev_attr_engine1_load.attr, + &dev_attr_engine1_leds.attr, + NULL +}; + +static struct attribute *lp5523_engine2_attributes[] = { + &dev_attr_engine2_load.attr, + &dev_attr_engine2_leds.attr, + NULL +}; + +static struct attribute *lp5523_engine3_attributes[] = { + &dev_attr_engine3_load.attr, + &dev_attr_engine3_leds.attr, + NULL +}; + +static const struct attribute_group lp5523_group = { + .attrs = lp5523_attributes, +}; + +static const struct attribute_group lp5523_engine_group[] = { + {.attrs = lp5523_engine1_attributes }, + {.attrs = lp5523_engine2_attributes }, + {.attrs = lp5523_engine3_attributes }, +}; + +static int lp5523_register_sysfs(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + + ret = sysfs_create_group(&dev->kobj, &lp5523_group); + if (ret < 0) + return ret; + + return 0; +} + +static void lp5523_unregister_sysfs(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int i; + + sysfs_remove_group(&dev->kobj, &lp5523_group); + + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) + if (chip->engines[i].mode == LP5523_CMD_LOAD) + sysfs_remove_group(&dev->kobj, &lp5523_engine_group[i]); + + for (i = 0; i < chip->num_leds; i++) + sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, + &lp5523_led_attribute_group); +} + +/*--------------------------------------------------------------*/ +/* Set chip operating mode */ +/*--------------------------------------------------------------*/ +static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode) +{ + /* engine to chip */ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + struct device *dev = &client->dev; + int ret = 0; + + /* if in that mode already do nothing, except for run */ + if (mode == engine->mode && mode != LP5523_CMD_RUN) + return 0; + + if (mode == LP5523_CMD_RUN) { + ret = lp5523_run_program(engine); + } else if (mode == LP5523_CMD_LOAD) { + lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); + lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret = sysfs_create_group(&dev->kobj, engine->attributes); + if (ret) + return ret; + } else if (mode == LP5523_CMD_DISABLED) { + lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); + } + + /* remove load attribute from sysfs if not in load mode */ + if (engine->mode == LP5523_CMD_LOAD && mode != LP5523_CMD_LOAD) + sysfs_remove_group(&dev->kobj, engine->attributes); + + engine->mode = mode; + + return ret; +} + +/*--------------------------------------------------------------*/ +/* Probe, Attach, Remove */ +/*--------------------------------------------------------------*/ +static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) +{ + if (id < 1 || id > LP5523_ENGINES) + return -1; + engine->id = id; + engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); + engine->prog_page = id - 1; + engine->mux_page = id + 2; + engine->attributes = &lp5523_engine_group[id - 1]; + + return 0; +} + +static int __init lp5523_init_led(struct lp5523_led *led, struct device *dev, + int chan, struct lp5523_platform_data *pdata) +{ + char name[32]; + int res; + + if (chan >= LP5523_LEDS) + return -EINVAL; + + if (pdata->led_config[chan].led_current) { + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= LP5523_LEDS) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + LP5523_LEDS - 1); + return -EINVAL; + } + + snprintf(name, 32, "lp5523:channel%d", chan); + + led->cdev.name = name; + led->cdev.brightness_set = lp5523_set_brightness; + res = led_classdev_register(dev, &led->cdev); + if (res < 0) { + dev_err(dev, "couldn't register led on channel %d\n", + chan); + return res; + } + res = sysfs_create_group(&led->cdev.dev->kobj, + &lp5523_led_attribute_group); + if (res < 0) { + dev_err(dev, "couldn't register current attribute\n"); + led_classdev_unregister(&led->cdev); + return res; + } + } else { + led->led_current = 0; + } + return 0; +} + +static struct i2c_driver lp5523_driver; + +static int lp5523_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lp5523_chip *chip; + struct lp5523_platform_data *pdata; + int ret, i, led; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto fail1; + } + + mutex_init(&chip->lock); + + chip->pdata = pdata; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + goto fail1; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 10000); + pdata->enable(1); + usleep_range(1000, 10000); /* Spec says min 500us */ + } + + ret = lp5523_detect(client); + if (ret) + goto fail2; + + dev_info(&client->dev, "LP5523 Programmable led chip found\n"); + + /* Initialize engines */ + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + ret = lp5523_init_engine(&chip->engines[i], i + 1); + if (ret) { + dev_err(&client->dev, "error initializing engine\n"); + goto fail2; + } + } + ret = lp5523_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto fail2; + } + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + goto fail3; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set LED current */ + lp5523_write(client, + LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5523_led_brightness_work); + + led++; + } + + ret = lp5523_register_sysfs(client); + if (ret) { + dev_err(&client->dev, "registering sysfs failed\n"); + goto fail3; + } + return ret; +fail3: + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +fail2: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +fail1: + kfree(chip); + return ret; +} + +static int lp5523_remove(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + int i; + + lp5523_unregister_sysfs(client); + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } + + if (chip->pdata->enable) + chip->pdata->enable(0); + if (chip->pdata->release_resources) + chip->pdata->release_resources(); + kfree(chip); + return 0; +} + +static const struct i2c_device_id lp5523_id[] = { + { "lp5523", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lp5523_id); + +static struct i2c_driver lp5523_driver = { + .driver = { + .name = "lp5523", + }, + .probe = lp5523_probe, + .remove = lp5523_remove, + .id_table = lp5523_id, +}; + +static int __init lp5523_init(void) +{ + int ret; + + ret = i2c_add_driver(&lp5523_driver); + + if (ret < 0) + printk(KERN_ALERT "Adding lp5523 driver failed\n"); + + return ret; +} + +static void __exit lp5523_exit(void) +{ + i2c_del_driver(&lp5523_driver); +} + +module_init(lp5523_init); +module_exit(lp5523_exit); + +MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>"); +MODULE_DESCRIPTION("LP5523 LED engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 82b77bd482ff..b09bcbeade9c 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -12,73 +12,25 @@ */ #include <linux/module.h> -#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/list.h> -#include <linux/spinlock.h> #include <linux/device.h> -#include <linux/sysdev.h> -#include <linux/timer.h> #include <linux/ctype.h> #include <linux/leds.h> -#include <linux/slab.h> #include "leds.h" -struct timer_trig_data { - int brightness_on; /* LED brightness during "on" period. - * (LED_OFF < brightness_on <= LED_FULL) - */ - unsigned long delay_on; /* milliseconds on */ - unsigned long delay_off; /* milliseconds off */ - struct timer_list timer; -}; - -static void led_timer_function(unsigned long data) -{ - struct led_classdev *led_cdev = (struct led_classdev *) data; - struct timer_trig_data *timer_data = led_cdev->trigger_data; - unsigned long brightness; - unsigned long delay; - - if (!timer_data->delay_on || !timer_data->delay_off) { - led_set_brightness(led_cdev, LED_OFF); - return; - } - - brightness = led_get_brightness(led_cdev); - if (!brightness) { - /* Time to switch the LED on. */ - brightness = timer_data->brightness_on; - delay = timer_data->delay_on; - } else { - /* Store the current brightness value to be able - * to restore it when the delay_off period is over. - */ - timer_data->brightness_on = brightness; - brightness = LED_OFF; - delay = timer_data->delay_off; - } - - led_set_brightness(led_cdev, brightness); - - mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay)); -} - static ssize_t led_delay_on_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; - return sprintf(buf, "%lu\n", timer_data->delay_on); + return sprintf(buf, "%lu\n", led_cdev->blink_delay_on); } static ssize_t led_delay_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); @@ -88,21 +40,7 @@ static ssize_t led_delay_on_store(struct device *dev, count++; if (count == size) { - if (timer_data->delay_on != state) { - /* the new value differs from the previous */ - timer_data->delay_on = state; - - /* deactivate previous settings */ - del_timer_sync(&timer_data->timer); - - /* try to activate hardware acceleration, if any */ - if (!led_cdev->blink_set || - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off)) { - /* no hardware acceleration, blink via timer */ - mod_timer(&timer_data->timer, jiffies + 1); - } - } + led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off); ret = count; } @@ -113,16 +51,14 @@ static ssize_t led_delay_off_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; - return sprintf(buf, "%lu\n", timer_data->delay_off); + return sprintf(buf, "%lu\n", led_cdev->blink_delay_off); } static ssize_t led_delay_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); @@ -132,21 +68,7 @@ static ssize_t led_delay_off_store(struct device *dev, count++; if (count == size) { - if (timer_data->delay_off != state) { - /* the new value differs from the previous */ - timer_data->delay_off = state; - - /* deactivate previous settings */ - del_timer_sync(&timer_data->timer); - - /* try to activate hardware acceleration, if any */ - if (!led_cdev->blink_set || - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off)) { - /* no hardware acceleration, blink via timer */ - mod_timer(&timer_data->timer, jiffies + 1); - } - } + led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state); ret = count; } @@ -158,60 +80,34 @@ static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); static void timer_trig_activate(struct led_classdev *led_cdev) { - struct timer_trig_data *timer_data; int rc; - timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL); - if (!timer_data) - return; - - timer_data->brightness_on = led_get_brightness(led_cdev); - if (timer_data->brightness_on == LED_OFF) - timer_data->brightness_on = led_cdev->max_brightness; - led_cdev->trigger_data = timer_data; - - init_timer(&timer_data->timer); - timer_data->timer.function = led_timer_function; - timer_data->timer.data = (unsigned long) led_cdev; + led_cdev->trigger_data = NULL; rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); if (rc) - goto err_out; + return; rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); if (rc) goto err_out_delayon; - /* If there is hardware support for blinking, start one - * user friendly blink rate chosen by the driver. - */ - if (led_cdev->blink_set) - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off); + led_cdev->trigger_data = (void *)1; return; err_out_delayon: device_remove_file(led_cdev->dev, &dev_attr_delay_on); -err_out: - led_cdev->trigger_data = NULL; - kfree(timer_data); } static void timer_trig_deactivate(struct led_classdev *led_cdev) { - struct timer_trig_data *timer_data = led_cdev->trigger_data; - unsigned long on = 0, off = 0; - - if (timer_data) { + if (led_cdev->trigger_data) { device_remove_file(led_cdev->dev, &dev_attr_delay_on); device_remove_file(led_cdev->dev, &dev_attr_delay_off); - del_timer_sync(&timer_data->timer); - kfree(timer_data); } - /* If there is hardware support for blinking, stop it */ - if (led_cdev->blink_set) - led_cdev->blink_set(led_cdev, &on, &off); + /* Stop blinking */ + led_brightness_set(led_cdev, LED_OFF); } static struct led_trigger timer_led_trigger = { diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index 444696625171..f5f4da3d0b67 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -80,7 +80,7 @@ static void adb_iop_end_req(struct adb_request *req, int state) static void adb_iop_complete(struct iop_msg *msg) { struct adb_request *req; - uint flags; + unsigned long flags; local_irq_save(flags); @@ -103,7 +103,7 @@ static void adb_iop_listen(struct iop_msg *msg) { struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message; struct adb_request *req; - uint flags; + unsigned long flags; #ifdef DEBUG_ADB_IOP int i; #endif diff --git a/drivers/md/md.c b/drivers/md/md.c index 4e957f3140a8..324a3663fcda 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -706,7 +706,7 @@ static struct mdk_personality *find_pers(int level, char *clevel) /* return the offset of the super block in 512byte sectors */ static inline sector_t calc_dev_sboffset(struct block_device *bdev) { - sector_t num_sectors = bdev->bd_inode->i_size / 512; + sector_t num_sectors = i_size_read(bdev->bd_inode) / 512; return MD_NEW_SIZE_SECTORS(num_sectors); } @@ -1386,7 +1386,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) */ switch(minor_version) { case 0: - sb_start = rdev->bdev->bd_inode->i_size >> 9; + sb_start = i_size_read(rdev->bdev->bd_inode) >> 9; sb_start -= 8*2; sb_start &= ~(sector_t)(4*2-1); break; @@ -1472,7 +1472,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) ret = 0; } if (minor_version) - rdev->sectors = (rdev->bdev->bd_inode->i_size >> 9) - + rdev->sectors = (i_size_read(rdev->bdev->bd_inode) >> 9) - le64_to_cpu(sb->data_offset); else rdev->sectors = rdev->sb_start; @@ -1680,7 +1680,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors) return 0; /* component must fit device */ if (rdev->sb_start < rdev->data_offset) { /* minor versions 1 and 2; superblock before data */ - max_sectors = rdev->bdev->bd_inode->i_size >> 9; + max_sectors = i_size_read(rdev->bdev->bd_inode) >> 9; max_sectors -= rdev->data_offset; if (!num_sectors || num_sectors > max_sectors) num_sectors = max_sectors; @@ -1690,7 +1690,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors) } else { /* minor version 0; superblock after data */ sector_t sb_start; - sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; + sb_start = (i_size_read(rdev->bdev->bd_inode) >> 9) - 8*2; sb_start &= ~(sector_t)(4*2 - 1); max_sectors = rdev->sectors + sb_start - rdev->sb_start; if (!num_sectors || num_sectors > max_sectors) @@ -2584,7 +2584,7 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (!sectors) return -EBUSY; } else if (!sectors) - sectors = (rdev->bdev->bd_inode->i_size >> 9) - + sectors = (i_size_read(rdev->bdev->bd_inode) >> 9) - rdev->data_offset; } if (sectors < my_mddev->dev_sectors) @@ -2797,7 +2797,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi kobject_init(&rdev->kobj, &rdev_ktype); - size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + size = i_size_read(rdev->bdev->bd_inode) >> BLOCK_SIZE_BITS; if (!size) { printk(KERN_WARNING "md: %s has zero or unknown size, marking faulty!\n", @@ -5235,8 +5235,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); - rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; - } else + rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512; + } else rdev->sb_start = calc_dev_sboffset(rdev->bdev); rdev->sectors = rdev->sb_start; @@ -5306,7 +5306,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) if (mddev->persistent) rdev->sb_start = calc_dev_sboffset(rdev->bdev); else - rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; + rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512; rdev->sectors = rdev->sb_start; diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index f9b91ba8900c..0ed09358027e 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -123,7 +123,7 @@ static ssize_t als_sensing_range_store(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct als_data *data = i2c_get_clientdata(client); - unsigned int ret_val; + int ret_val; unsigned long val; if (strict_strtoul(buf, 10, &val)) diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index cee632e645e1..d79a972f2c79 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -649,7 +649,7 @@ static ssize_t bh1770_power_state_store(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; - size_t ret; + ssize_t ret; if (strict_strtoul(buf, 0, &value)) return -EINVAL; @@ -659,8 +659,12 @@ static ssize_t bh1770_power_state_store(struct device *dev, pm_runtime_get_sync(dev); ret = bh1770_lux_rate(chip, chip->lux_rate_index); - ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE); + if (ret < 0) { + pm_runtime_put(dev); + goto leave; + } + ret = bh1770_lux_interrupt_control(chip, BH1770_ENABLE); if (ret < 0) { pm_runtime_put(dev); goto leave; diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 34fe835921c4..ca47e6285075 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -87,7 +87,7 @@ static ssize_t als_sensing_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - unsigned int ret_val; + int ret_val; unsigned long val; if (strict_strtoul(buf, 10, &val)) @@ -106,6 +106,8 @@ static ssize_t als_sensing_range_store(struct device *dev, val = 4; ret_val = i2c_smbus_read_byte_data(client, 0x00); + if (ret_val < 0) + return ret_val; ret_val &= 0xFC; /*reset the bit before setting them */ ret_val |= val - 1; diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index eea1ef2f502b..4396d4b9bfb9 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -221,9 +221,6 @@ config RT2X00_LIB_LEDS boolean default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) -comment "rt2x00 leds support disabled due to modularized LEDS_CLASS and built-in rt2x00" - depends on RT2X00_LIB=y && LEDS_CLASS=m - config RT2X00_LIB_DEBUGFS bool "Ralink debugfs support" depends on RT2X00_LIB && MAC80211_DEBUGFS diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index a87c4985326e..3a5a6fcc0ead 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -13,7 +13,6 @@ #include <linux/spinlock.h> #include <linux/pci.h> #include <linux/msi.h> -#include <xen/xenbus.h> #include <xen/interface/io/pciif.h> #include <asm/xen/pci.h> #include <linux/interrupt.h> @@ -576,8 +575,9 @@ static pci_ers_result_t pcifront_common_process(int cmd, pcidev = pci_get_bus_and_slot(bus, devfn); if (!pcidev || !pcidev->driver) { - dev_err(&pcidev->dev, - "device or driver is NULL\n"); + dev_err(&pdev->xdev->dev, "device or AER driver is NULL\n"); + if (pcidev) + pci_dev_put(pcidev); return result; } pdrv = pcidev->driver; diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 68cf0c99138a..7b5080c45569 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -1159,11 +1159,11 @@ int __devinit rio_init_mports(void) list_for_each_entry(port, &rio_mports, node) { if (!request_mem_region(port->iores.start, - port->iores.end - port->iores.start, + resource_size(&port->iores), port->name)) { printk(KERN_ERR "RIO: Error requesting master port region 0x%016llx-0x%016llx\n", - (u64)port->iores.start, (u64)port->iores.end - 1); + (u64)port->iores.start, (u64)port->iores.end); rc = -ENOMEM; goto out; } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 1de30eb83bb0..f3cf924a2cd9 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -320,19 +320,11 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) "changed. The Linux SCSI layer does not " "automatically adjust these parameters.\n"); - if (scmd->request->cmd_flags & REQ_HARDBARRIER) - /* - * barrier requests should always retry on UA - * otherwise block will get a spurious error - */ - return NEEDS_RETRY; - else - /* - * for normal (non barrier) commands, pass the - * UA upwards for a determination in the - * completion functions - */ - return SUCCESS; + /* + * Pass the UA upwards for a determination in the completion + * functions. + */ + return SUCCESS; /* these three are not supported */ case COPY_ABORTED: diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index d4b711c9a416..3374618300af 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -18,6 +18,7 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/vt_kern.h> +#include <linux/input.h> #define MAX_CONFIG_LEN 40 @@ -37,6 +38,61 @@ static struct tty_driver *kgdb_tty_driver; static int kgdb_tty_line; #ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_reset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + input_reset_device(dev); + + /* Retrun an error - we do not want to bind, just to reset */ + return -ENODEV; +} + +static void kgdboc_reset_disconnect(struct input_handle *handle) +{ + /* We do not expect anyone to actually bind to us */ + BUG(); +} + +static const struct input_device_id kgdboc_reset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { } +}; + +static struct input_handler kgdboc_reset_handler = { + .connect = kgdboc_reset_connect, + .disconnect = kgdboc_reset_disconnect, + .name = "kgdboc_reset", + .id_table = kgdboc_reset_ids, +}; + +static DEFINE_MUTEX(kgdboc_reset_mutex); + +static void kgdboc_restore_input_helper(struct work_struct *dummy) +{ + /* + * We need to take a mutex to prevent several instances of + * this work running on different CPUs so they don't try + * to register again already registered handler. + */ + mutex_lock(&kgdboc_reset_mutex); + + if (input_register_handler(&kgdboc_reset_handler) == 0) + input_unregister_handler(&kgdboc_reset_handler); + + mutex_unlock(&kgdboc_reset_mutex); +} + +static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); + +static void kgdboc_restore_input(void) +{ + schedule_work(&kgdboc_restore_input_work); +} + static int kgdboc_register_kbd(char **cptr) { if (strncmp(*cptr, "kbd", 3) == 0) { @@ -64,10 +120,12 @@ static void kgdboc_unregister_kbd(void) i--; } } + flush_work_sync(&kgdboc_restore_input_work); } #else /* ! CONFIG_KDB_KEYBOARD */ #define kgdboc_register_kbd(x) 0 #define kgdboc_unregister_kbd() +#define kgdboc_restore_input() #endif /* ! CONFIG_KDB_KEYBOARD */ static int kgdboc_option_setup(char *opt) @@ -231,6 +289,7 @@ static void kgdboc_post_exp_handler(void) dbg_restore_graphics = 0; con_debug_leave(); } + kgdboc_restore_input(); } static struct kgdb_io kgdboc_io_ops = { diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 2054b1e25a65..d1268191acbd 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -331,10 +331,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, iu->iu_id = IU_ID_COMMAND; iu->tag = cpu_to_be16(stream_id); - if (sdev->ordered_tags && (cmnd->request->cmd_flags & REQ_HARDBARRIER)) - iu->prio_attr = UAS_ORDERED_TAG; - else - iu->prio_attr = UAS_SIMPLE_TAG; + iu->prio_attr = UAS_SIMPLE_TAG; iu->len = len; int_to_scsilun(sdev->lun, &iu->lun); memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 3ec24609151e..734c650a47c4 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -502,8 +502,10 @@ static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct adp8860_bl *data = dev_get_drvdata(dev); + int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); + if (ret) + return ret; - strict_strtoul(buf, 10, &data->cached_daylight_max); return adp8860_store(dev, buf, count, ADP8860_BLMX1); } static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, @@ -614,7 +616,7 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, if (val == 0) { /* Enable automatic ambient light sensing */ adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); - } else if ((val > 0) && (val < 6)) { + } else if ((val > 0) && (val <= 3)) { /* Disable automatic ambient light sensing */ adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); @@ -622,7 +624,7 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, mutex_lock(&data->lock); adp8860_read(data->client, ADP8860_CFGR, ®_val); reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); - reg_val |= val << CFGR_BLV_SHIFT; + reg_val |= (val - 1) << CFGR_BLV_SHIFT; adp8860_write(data->client, ADP8860_CFGR, reg_val); mutex_unlock(&data->lock); } diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 9093ef0fa869..c67801e57aaf 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -78,7 +78,7 @@ static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) const u16 slpin = 0x10; const u16 disoff = 0x28; - if (power) { + if (power <= FB_BLANK_NORMAL) { if (priv->lcd_on) return 0; diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index abc43a0eb97d..5d3cf33953ac 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -129,7 +129,7 @@ static int lms283gf05_power_set(struct lcd_device *ld, int power) struct spi_device *spi = st->spi; struct lms283gf05_pdata *pdata = spi->dev.platform_data; - if (power) { + if (power <= FB_BLANK_NORMAL) { if (pdata) lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted); diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 9fb533f6373e..1485f7345f49 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -335,6 +335,24 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, .driver_data = (void *)&nvidia_chipset_data, }, + { + .callback = mbp_dmi_match, + .ident = "MacBookAir 3,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookAir 3,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,2"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, { } }; diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 550443518891..21866ec69656 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -25,6 +25,7 @@ struct pwm_bl_data { struct pwm_device *pwm; struct device *dev; unsigned int period; + unsigned int lth_brightness; int (*notify)(struct device *, int brightness); }; @@ -48,7 +49,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); } else { - pwm_config(pb->pwm, brightness * pb->period / max, pb->period); + brightness = pb->lth_brightness + + (brightness * (pb->period - pb->lth_brightness) / max); + pwm_config(pb->pwm, brightness, pb->period); pwm_enable(pb->pwm); } return 0; @@ -92,6 +95,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->period = data->pwm_period_ns; pb->notify = data->notify; + pb->lth_brightness = data->lth_brightness * + (data->pwm_period_ns / data->max_brightness); pb->dev = &pdev->dev; pb->pwm = pwm_request(data->pwm_id, "backlight"); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index a3128c9cb7ad..5927db0da999 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -729,10 +729,10 @@ static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev, return strlen(buf); } -static DEVICE_ATTR(gamma_table, 0644, +static DEVICE_ATTR(gamma_table, 0444, s6e63m0_sysfs_show_gamma_table, NULL); -static int __init s6e63m0_probe(struct spi_device *spi) +static int __devinit s6e63m0_probe(struct spi_device *spi) { int ret = 0; struct s6e63m0 *lcd = NULL; @@ -829,6 +829,9 @@ static int __devexit s6e63m0_remove(struct spi_device *spi) struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); + device_remove_file(&spi->dev, &dev_attr_gamma_table); + device_remove_file(&spi->dev, &dev_attr_gamma_mode); + backlight_device_unregister(lcd->bd); lcd_device_unregister(lcd->ld); kfree(lcd); diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 97612f548a8e..321a0c8346e5 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -1299,9 +1299,6 @@ static void restore_cpu_virqs(unsigned int cpu) evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_virq_info(evtchn, virq); bind_evtchn_to_cpu(evtchn, cpu); - - /* Ready for use. */ - unmask_evtchn(evtchn); } } @@ -1327,10 +1324,6 @@ static void restore_cpu_ipis(unsigned int cpu) evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_ipi_info(evtchn, ipi); bind_evtchn_to_cpu(evtchn, cpu); - - /* Ready for use. */ - unmask_evtchn(evtchn); - } } @@ -1390,6 +1383,7 @@ void xen_poll_irq(int irq) void xen_irq_resume(void) { unsigned int cpu, irq, evtchn; + struct irq_desc *desc; init_evtchn_cpu_bindings(); @@ -1408,6 +1402,23 @@ void xen_irq_resume(void) restore_cpu_virqs(cpu); restore_cpu_ipis(cpu); } + + /* + * Unmask any IRQF_NO_SUSPEND IRQs which are enabled. These + * are not handled by the IRQ core. + */ + for_each_irq_desc(irq, desc) { + if (!desc->action || !(desc->action->flags & IRQF_NO_SUSPEND)) + continue; + if (desc->status & IRQ_DISABLED) + continue; + + evtchn = evtchn_from_irq(irq); + if (evtchn == -1) + continue; + + unmask_evtchn(evtchn); + } } static struct irq_chip xen_dynamic_chip __read_mostly = { |