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