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) { 37289c0522SBrian Norris pr_debug( "MTD %s(): chip->start: %lx wanted >= 0x400000\n", 381da177e4SLinus Torvalds __func__, chip->start ); 391da177e4SLinus Torvalds return -EIO; 401da177e4SLinus Torvalds } 411da177e4SLinus Torvalds /* 421da177e4SLinus Torvalds * lock block registers: 431da177e4SLinus Torvalds * - on 64k boundariesand 441da177e4SLinus Torvalds * - bit 1 set high 451da177e4SLinus Torvalds * - block lock registers are 4MiB lower - overflow subtract (danger) 461da177e4SLinus Torvalds * 471da177e4SLinus Torvalds * The address manipulation is first done on the logical address 481da177e4SLinus Torvalds * which is 0 at the start of the chip, and then the offset of 491da177e4SLinus Torvalds * the individual chip is addted to it. Any other order a weird 501da177e4SLinus Torvalds * map offset could cause problems. 511da177e4SLinus Torvalds */ 521da177e4SLinus Torvalds adr = (adr & ~0xffffUL) | 0x2; 531da177e4SLinus Torvalds adr += chip->start - 0x400000; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* 561da177e4SLinus Torvalds * This is easy because these are writes to registers and not writes 571da177e4SLinus Torvalds * to flash memory - that means that we don't have to check status 581da177e4SLinus Torvalds * and timeout. 591da177e4SLinus Torvalds */ 60c4e77376SStefani Seibold mutex_lock(&chip->mutex); 611da177e4SLinus Torvalds ret = get_chip(map, chip, adr, FL_LOCKING); 621da177e4SLinus Torvalds if (ret) { 63c4e77376SStefani Seibold mutex_unlock(&chip->mutex); 641da177e4SLinus Torvalds return ret; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 67e6be133bSShashi Rao chip->oldstate = chip->state; 681da177e4SLinus Torvalds chip->state = xxlt->state; 691da177e4SLinus Torvalds map_write(map, CMD(xxlt->val), adr); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* Done and happy. */ 72e6be133bSShashi Rao chip->state = chip->oldstate; 731da177e4SLinus Torvalds put_chip(map, chip, adr); 74c4e77376SStefani Seibold mutex_unlock(&chip->mutex); 751da177e4SLinus Torvalds return 0; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds 7969423d99SAdrian Hunter static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 801da177e4SLinus Torvalds { 811da177e4SLinus Torvalds int ret; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 841da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds return ret; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds 9069423d99SAdrian Hunter static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds int ret; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 951da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds return ret; 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 100cc318222SGuillaume LECERF static void fixup_use_fwh_lock(struct mtd_info *mtd) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds printk(KERN_NOTICE "using fwh lock/unlock method\n"); 1031da177e4SLinus Torvalds /* Setup for the chips with the fwh lock method */ 104*3c3c10bbSArtem Bityutskiy mtd->_lock = fwh_lock_varsize; 105*3c3c10bbSArtem Bityutskiy mtd->_unlock = fwh_unlock_varsize; 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds #endif /* FWH_LOCK_H */ 108