162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef FWH_LOCK_H
362306a36Sopenharmony_ci#define FWH_LOCK_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci
662306a36Sopenharmony_cienum fwh_lock_state {
762306a36Sopenharmony_ci        FWH_UNLOCKED   = 0,
862306a36Sopenharmony_ci	FWH_DENY_WRITE = 1,
962306a36Sopenharmony_ci	FWH_IMMUTABLE  = 2,
1062306a36Sopenharmony_ci	FWH_DENY_READ  = 4,
1162306a36Sopenharmony_ci};
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistruct fwh_xxlock_thunk {
1462306a36Sopenharmony_ci	enum fwh_lock_state val;
1562306a36Sopenharmony_ci	flstate_t state;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define FWH_XXLOCK_ONEBLOCK_LOCK   ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
2062306a36Sopenharmony_ci#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED,   FL_UNLOCKING})
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * This locking/unlock is specific to firmware hub parts.  Only one
2462306a36Sopenharmony_ci * is known that supports the Intel command set.    Firmware
2562306a36Sopenharmony_ci * hub parts cannot be interleaved as they are on the LPC bus
2662306a36Sopenharmony_ci * so this code has not been tested with interleaved chips,
2762306a36Sopenharmony_ci * and will likely fail in that context.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
3062306a36Sopenharmony_ci	unsigned long adr, int len, void *thunk)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3362306a36Sopenharmony_ci	struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
3462306a36Sopenharmony_ci	int ret;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* Refuse the operation if the we cannot look behind the chip */
3762306a36Sopenharmony_ci	if (chip->start < 0x400000) {
3862306a36Sopenharmony_ci		pr_debug( "MTD %s(): chip->start: %lx wanted >= 0x400000\n",
3962306a36Sopenharmony_ci			__func__, chip->start );
4062306a36Sopenharmony_ci		return -EIO;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * lock block registers:
4462306a36Sopenharmony_ci	 * - on 64k boundariesand
4562306a36Sopenharmony_ci	 * - bit 1 set high
4662306a36Sopenharmony_ci	 * - block lock registers are 4MiB lower - overflow subtract (danger)
4762306a36Sopenharmony_ci	 *
4862306a36Sopenharmony_ci	 * The address manipulation is first done on the logical address
4962306a36Sopenharmony_ci	 * which is 0 at the start of the chip, and then the offset of
5062306a36Sopenharmony_ci	 * the individual chip is addted to it.  Any other order a weird
5162306a36Sopenharmony_ci	 * map offset could cause problems.
5262306a36Sopenharmony_ci	 */
5362306a36Sopenharmony_ci	adr = (adr & ~0xffffUL) | 0x2;
5462306a36Sopenharmony_ci	adr += chip->start - 0x400000;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/*
5762306a36Sopenharmony_ci	 * This is easy because these are writes to registers and not writes
5862306a36Sopenharmony_ci	 * to flash memory - that means that we don't have to check status
5962306a36Sopenharmony_ci	 * and timeout.
6062306a36Sopenharmony_ci	 */
6162306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
6262306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_LOCKING);
6362306a36Sopenharmony_ci	if (ret) {
6462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
6562306a36Sopenharmony_ci		return ret;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	chip->oldstate = chip->state;
6962306a36Sopenharmony_ci	chip->state = xxlt->state;
7062306a36Sopenharmony_ci	map_write(map, CMD(xxlt->val), adr);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Done and happy. */
7362306a36Sopenharmony_ci	chip->state = chip->oldstate;
7462306a36Sopenharmony_ci	put_chip(map, chip, adr);
7562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	int ret;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
8562306a36Sopenharmony_ci		(void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return ret;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
9662306a36Sopenharmony_ci		(void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return ret;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void fixup_use_fwh_lock(struct mtd_info *mtd)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	printk(KERN_NOTICE "using fwh lock/unlock method\n");
10462306a36Sopenharmony_ci	/* Setup for the chips with the fwh lock method */
10562306a36Sopenharmony_ci	mtd->_lock   = fwh_lock_varsize;
10662306a36Sopenharmony_ci	mtd->_unlock = fwh_unlock_varsize;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci#endif /* FWH_LOCK_H */
109