18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef FWH_LOCK_H 38c2ecf20Sopenharmony_ci#define FWH_LOCK_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_cienum fwh_lock_state { 78c2ecf20Sopenharmony_ci FWH_UNLOCKED = 0, 88c2ecf20Sopenharmony_ci FWH_DENY_WRITE = 1, 98c2ecf20Sopenharmony_ci FWH_IMMUTABLE = 2, 108c2ecf20Sopenharmony_ci FWH_DENY_READ = 4, 118c2ecf20Sopenharmony_ci}; 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct fwh_xxlock_thunk { 148c2ecf20Sopenharmony_ci enum fwh_lock_state val; 158c2ecf20Sopenharmony_ci flstate_t state; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) 208c2ecf20Sopenharmony_ci#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * This locking/unlock is specific to firmware hub parts. Only one 248c2ecf20Sopenharmony_ci * is known that supports the Intel command set. Firmware 258c2ecf20Sopenharmony_ci * hub parts cannot be interleaved as they are on the LPC bus 268c2ecf20Sopenharmony_ci * so this code has not been tested with interleaved chips, 278c2ecf20Sopenharmony_ci * and will likely fail in that context. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 308c2ecf20Sopenharmony_ci unsigned long adr, int len, void *thunk) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 338c2ecf20Sopenharmony_ci struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* Refuse the operation if the we cannot look behind the chip */ 378c2ecf20Sopenharmony_ci if (chip->start < 0x400000) { 388c2ecf20Sopenharmony_ci pr_debug( "MTD %s(): chip->start: %lx wanted >= 0x400000\n", 398c2ecf20Sopenharmony_ci __func__, chip->start ); 408c2ecf20Sopenharmony_ci return -EIO; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci /* 438c2ecf20Sopenharmony_ci * lock block registers: 448c2ecf20Sopenharmony_ci * - on 64k boundariesand 458c2ecf20Sopenharmony_ci * - bit 1 set high 468c2ecf20Sopenharmony_ci * - block lock registers are 4MiB lower - overflow subtract (danger) 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * The address manipulation is first done on the logical address 498c2ecf20Sopenharmony_ci * which is 0 at the start of the chip, and then the offset of 508c2ecf20Sopenharmony_ci * the individual chip is addted to it. Any other order a weird 518c2ecf20Sopenharmony_ci * map offset could cause problems. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci adr = (adr & ~0xffffUL) | 0x2; 548c2ecf20Sopenharmony_ci adr += chip->start - 0x400000; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * This is easy because these are writes to registers and not writes 588c2ecf20Sopenharmony_ci * to flash memory - that means that we don't have to check status 598c2ecf20Sopenharmony_ci * and timeout. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 628c2ecf20Sopenharmony_ci ret = get_chip(map, chip, adr, FL_LOCKING); 638c2ecf20Sopenharmony_ci if (ret) { 648c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 698c2ecf20Sopenharmony_ci chip->state = xxlt->state; 708c2ecf20Sopenharmony_ci map_write(map, CMD(xxlt->val), adr); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Done and happy. */ 738c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 748c2ecf20Sopenharmony_ci put_chip(map, chip, adr); 758c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 858c2ecf20Sopenharmony_ci (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, 968c2ecf20Sopenharmony_ci (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void fixup_use_fwh_lock(struct mtd_info *mtd) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci printk(KERN_NOTICE "using fwh lock/unlock method\n"); 1048c2ecf20Sopenharmony_ci /* Setup for the chips with the fwh lock method */ 1058c2ecf20Sopenharmony_ci mtd->_lock = fwh_lock_varsize; 1068c2ecf20Sopenharmony_ci mtd->_unlock = fwh_unlock_varsize; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci#endif /* FWH_LOCK_H */ 109