11da177e4SLinus Torvalds #ifndef FWH_LOCK_H 21da177e4SLinus Torvalds #define FWH_LOCK_H 31da177e4SLinus Torvalds 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds enum fwh_lock_state { 61da177e4SLinus Torvalds FWH_UNLOCKED = 0, 71da177e4SLinus Torvalds FWH_DENY_WRITE = 1, 81da177e4SLinus Torvalds FWH_IMMUTABLE = 2, 91da177e4SLinus Torvalds FWH_DENY_READ = 4, 101da177e4SLinus Torvalds }; 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds struct fwh_xxlock_thunk { 131da177e4SLinus Torvalds enum fwh_lock_state val; 141da177e4SLinus Torvalds flstate_t state; 151da177e4SLinus Torvalds }; 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds #define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) 191da177e4SLinus Torvalds #define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds /* 221da177e4SLinus Torvalds * This locking/unlock is specific to firmware hub parts. Only one 231da177e4SLinus Torvalds * is known that supports the Intel command set. Firmware 241da177e4SLinus Torvalds * hub parts cannot be interleaved as they are on the LPC bus 251da177e4SLinus Torvalds * so this code has not been tested with interleaved chips, 261da177e4SLinus Torvalds * and will likely fail in that context. 271da177e4SLinus Torvalds */ 281da177e4SLinus Torvalds static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 291da177e4SLinus Torvalds unsigned long adr, int len, void *thunk) 301da177e4SLinus Torvalds { 311da177e4SLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 321da177e4SLinus Torvalds struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; 331da177e4SLinus Torvalds int ret; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* Refuse the operation if the we cannot look behind the chip */ 361da177e4SLinus Torvalds if (chip->start < 0x400000) { 371da177e4SLinus Torvalds DEBUG( MTD_DEBUG_LEVEL3, 381da177e4SLinus Torvalds "MTD %s(): chip->start: %lx wanted >= 0x400000\n", 391da177e4SLinus Torvalds __func__, chip->start ); 401da177e4SLinus Torvalds return -EIO; 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds /* 431da177e4SLinus Torvalds * lock block registers: 441da177e4SLinus Torvalds * - on 64k boundariesand 451da177e4SLinus Torvalds * - bit 1 set high 461da177e4SLinus Torvalds * - block lock registers are 4MiB lower - overflow subtract (danger) 471da177e4SLinus Torvalds * 481da177e4SLinus Torvalds * The address manipulation is first done on the logical address 491da177e4SLinus Torvalds * which is 0 at the start of the chip, and then the offset of 501da177e4SLinus Torvalds * the individual chip is addted to it. Any other order a weird 511da177e4SLinus Torvalds * map offset could cause problems. 521da177e4SLinus Torvalds */ 531da177e4SLinus Torvalds adr = (adr & ~0xffffUL) | 0x2; 541da177e4SLinus Torvalds adr += chip->start - 0x400000; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds /* 571da177e4SLinus Torvalds * This is easy because these are writes to registers and not writes 581da177e4SLinus Torvalds * to flash memory - that means that we don't have to check status 591da177e4SLinus Torvalds * and timeout. 601da177e4SLinus Torvalds */ 6102b15e34STodd Poynor spin_lock(chip->mutex); 621da177e4SLinus Torvalds ret = get_chip(map, chip, adr, FL_LOCKING); 631da177e4SLinus Torvalds if (ret) { 6402b15e34STodd Poynor spin_unlock(chip->mutex); 651da177e4SLinus Torvalds return ret; 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 68e6be133bSShashi Rao chip->oldstate = chip->state; 691da177e4SLinus Torvalds chip->state = xxlt->state; 701da177e4SLinus Torvalds map_write(map, CMD(xxlt->val), adr); 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds /* Done and happy. */ 73e6be133bSShashi Rao chip->state = chip->oldstate; 741da177e4SLinus Torvalds put_chip(map, chip, adr); 7502b15e34STodd Poynor spin_unlock(chip->mutex); 761da177e4SLinus Torvalds return 0; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds 80*69423d99SAdrian Hunter static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 811da177e4SLinus Torvalds { 821da177e4SLinus Torvalds int ret; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 851da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds return ret; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds 91*69423d99SAdrian Hunter static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds int ret; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 961da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds return ret; 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) 1021da177e4SLinus Torvalds { 1031da177e4SLinus Torvalds printk(KERN_NOTICE "using fwh lock/unlock method\n"); 1041da177e4SLinus Torvalds /* Setup for the chips with the fwh lock method */ 1051da177e4SLinus Torvalds mtd->lock = fwh_lock_varsize; 1061da177e4SLinus Torvalds mtd->unlock = fwh_unlock_varsize; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds #endif /* FWH_LOCK_H */ 109