1*1da177e4SLinus Torvalds #ifndef FWH_LOCK_H 2*1da177e4SLinus Torvalds #define FWH_LOCK_H 3*1da177e4SLinus Torvalds 4*1da177e4SLinus Torvalds 5*1da177e4SLinus Torvalds enum fwh_lock_state { 6*1da177e4SLinus Torvalds FWH_UNLOCKED = 0, 7*1da177e4SLinus Torvalds FWH_DENY_WRITE = 1, 8*1da177e4SLinus Torvalds FWH_IMMUTABLE = 2, 9*1da177e4SLinus Torvalds FWH_DENY_READ = 4, 10*1da177e4SLinus Torvalds }; 11*1da177e4SLinus Torvalds 12*1da177e4SLinus Torvalds struct fwh_xxlock_thunk { 13*1da177e4SLinus Torvalds enum fwh_lock_state val; 14*1da177e4SLinus Torvalds flstate_t state; 15*1da177e4SLinus Torvalds }; 16*1da177e4SLinus Torvalds 17*1da177e4SLinus Torvalds 18*1da177e4SLinus Torvalds #define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) 19*1da177e4SLinus Torvalds #define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) 20*1da177e4SLinus Torvalds 21*1da177e4SLinus Torvalds /* 22*1da177e4SLinus Torvalds * This locking/unlock is specific to firmware hub parts. Only one 23*1da177e4SLinus Torvalds * is known that supports the Intel command set. Firmware 24*1da177e4SLinus Torvalds * hub parts cannot be interleaved as they are on the LPC bus 25*1da177e4SLinus Torvalds * so this code has not been tested with interleaved chips, 26*1da177e4SLinus Torvalds * and will likely fail in that context. 27*1da177e4SLinus Torvalds */ 28*1da177e4SLinus Torvalds static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 29*1da177e4SLinus Torvalds unsigned long adr, int len, void *thunk) 30*1da177e4SLinus Torvalds { 31*1da177e4SLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 32*1da177e4SLinus Torvalds struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; 33*1da177e4SLinus Torvalds int ret; 34*1da177e4SLinus Torvalds 35*1da177e4SLinus Torvalds /* Refuse the operation if the we cannot look behind the chip */ 36*1da177e4SLinus Torvalds if (chip->start < 0x400000) { 37*1da177e4SLinus Torvalds DEBUG( MTD_DEBUG_LEVEL3, 38*1da177e4SLinus Torvalds "MTD %s(): chip->start: %lx wanted >= 0x400000\n", 39*1da177e4SLinus Torvalds __func__, chip->start ); 40*1da177e4SLinus Torvalds return -EIO; 41*1da177e4SLinus Torvalds } 42*1da177e4SLinus Torvalds /* 43*1da177e4SLinus Torvalds * lock block registers: 44*1da177e4SLinus Torvalds * - on 64k boundariesand 45*1da177e4SLinus Torvalds * - bit 1 set high 46*1da177e4SLinus Torvalds * - block lock registers are 4MiB lower - overflow subtract (danger) 47*1da177e4SLinus Torvalds * 48*1da177e4SLinus Torvalds * The address manipulation is first done on the logical address 49*1da177e4SLinus Torvalds * which is 0 at the start of the chip, and then the offset of 50*1da177e4SLinus Torvalds * the individual chip is addted to it. Any other order a weird 51*1da177e4SLinus Torvalds * map offset could cause problems. 52*1da177e4SLinus Torvalds */ 53*1da177e4SLinus Torvalds adr = (adr & ~0xffffUL) | 0x2; 54*1da177e4SLinus Torvalds adr += chip->start - 0x400000; 55*1da177e4SLinus Torvalds 56*1da177e4SLinus Torvalds /* 57*1da177e4SLinus Torvalds * This is easy because these are writes to registers and not writes 58*1da177e4SLinus Torvalds * to flash memory - that means that we don't have to check status 59*1da177e4SLinus Torvalds * and timeout. 60*1da177e4SLinus Torvalds */ 61*1da177e4SLinus Torvalds cfi_spin_lock(chip->mutex); 62*1da177e4SLinus Torvalds ret = get_chip(map, chip, adr, FL_LOCKING); 63*1da177e4SLinus Torvalds if (ret) { 64*1da177e4SLinus Torvalds cfi_spin_unlock(chip->mutex); 65*1da177e4SLinus Torvalds return ret; 66*1da177e4SLinus Torvalds } 67*1da177e4SLinus Torvalds 68*1da177e4SLinus Torvalds chip->state = xxlt->state; 69*1da177e4SLinus Torvalds map_write(map, CMD(xxlt->val), adr); 70*1da177e4SLinus Torvalds 71*1da177e4SLinus Torvalds /* Done and happy. */ 72*1da177e4SLinus Torvalds chip->state = FL_READY; 73*1da177e4SLinus Torvalds put_chip(map, chip, adr); 74*1da177e4SLinus Torvalds cfi_spin_unlock(chip->mutex); 75*1da177e4SLinus Torvalds return 0; 76*1da177e4SLinus Torvalds } 77*1da177e4SLinus Torvalds 78*1da177e4SLinus Torvalds 79*1da177e4SLinus Torvalds static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) 80*1da177e4SLinus Torvalds { 81*1da177e4SLinus Torvalds int ret; 82*1da177e4SLinus Torvalds 83*1da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 84*1da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); 85*1da177e4SLinus Torvalds 86*1da177e4SLinus Torvalds return ret; 87*1da177e4SLinus Torvalds } 88*1da177e4SLinus Torvalds 89*1da177e4SLinus Torvalds 90*1da177e4SLinus Torvalds static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) 91*1da177e4SLinus Torvalds { 92*1da177e4SLinus Torvalds int ret; 93*1da177e4SLinus Torvalds 94*1da177e4SLinus Torvalds ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 95*1da177e4SLinus Torvalds (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); 96*1da177e4SLinus Torvalds 97*1da177e4SLinus Torvalds return ret; 98*1da177e4SLinus Torvalds } 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) 101*1da177e4SLinus Torvalds { 102*1da177e4SLinus Torvalds printk(KERN_NOTICE "using fwh lock/unlock method\n"); 103*1da177e4SLinus Torvalds /* Setup for the chips with the fwh lock method */ 104*1da177e4SLinus Torvalds mtd->lock = fwh_lock_varsize; 105*1da177e4SLinus Torvalds mtd->unlock = fwh_unlock_varsize; 106*1da177e4SLinus Torvalds } 107*1da177e4SLinus Torvalds #endif /* FWH_LOCK_H */ 108