xref: /kernel/linux/linux-6.6/arch/m68k/mm/hwtest.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Tests for presence or absence of hardware registers.
362306a36Sopenharmony_ci * This code was originally in atari/config.c, but I noticed
462306a36Sopenharmony_ci * that it was also in drivers/nubus/nubus.c and I wanted to
562306a36Sopenharmony_ci * use it in hp300/config.c, so it seemed sensible to pull it
662306a36Sopenharmony_ci * out into its own file.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * The test is for use when trying to read a hardware register
962306a36Sopenharmony_ci * that isn't present would cause a bus error. We set up a
1062306a36Sopenharmony_ci * temporary handler so that this doesn't kill the kernel.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * There is a test-by-reading and a test-by-writing; I present
1362306a36Sopenharmony_ci * them here complete with the comments from the original atari
1462306a36Sopenharmony_ci * config.c...
1562306a36Sopenharmony_ci *                -- PMM <pmaydell@chiark.greenend.org.uk>, 05/1998
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* This function tests for the presence of an address, specially a
1962306a36Sopenharmony_ci * hardware register address. It is called very early in the kernel
2062306a36Sopenharmony_ci * initialization process, when the VBR register isn't set up yet. On
2162306a36Sopenharmony_ci * an Atari, it still points to address 0, which is unmapped. So a bus
2262306a36Sopenharmony_ci * error would cause another bus error while fetching the exception
2362306a36Sopenharmony_ci * vector, and the CPU would do nothing at all. So we needed to set up
2462306a36Sopenharmony_ci * a temporary VBR and a vector table for the duration of the test.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciint hwreg_present(volatile void *regp)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int ret = 0;
3262306a36Sopenharmony_ci	unsigned long flags;
3362306a36Sopenharmony_ci	long save_sp, save_vbr;
3462306a36Sopenharmony_ci	long tmp_vectors[3];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	local_irq_save(flags);
3762306a36Sopenharmony_ci	__asm__ __volatile__ (
3862306a36Sopenharmony_ci		"movec %/vbr,%2\n\t"
3962306a36Sopenharmony_ci		"movel #Lberr1,%4@(8)\n\t"
4062306a36Sopenharmony_ci		"movec %4,%/vbr\n\t"
4162306a36Sopenharmony_ci		"movel %/sp,%1\n\t"
4262306a36Sopenharmony_ci		"moveq #0,%0\n\t"
4362306a36Sopenharmony_ci		"tstb %3@\n\t"
4462306a36Sopenharmony_ci		"nop\n\t"
4562306a36Sopenharmony_ci		"moveq #1,%0\n"
4662306a36Sopenharmony_ci	"Lberr1:\n\t"
4762306a36Sopenharmony_ci		"movel %1,%/sp\n\t"
4862306a36Sopenharmony_ci		"movec %2,%/vbr"
4962306a36Sopenharmony_ci		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
5062306a36Sopenharmony_ci		: "a" (regp), "a" (tmp_vectors)
5162306a36Sopenharmony_ci	);
5262306a36Sopenharmony_ci	local_irq_restore(flags);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return ret;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ciEXPORT_SYMBOL(hwreg_present);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Basically the same, but writes a value into a word register, protected
5962306a36Sopenharmony_ci * by a bus error handler. Returns 1 if successful, 0 otherwise.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint hwreg_write(volatile void *regp, unsigned short val)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int ret;
6562306a36Sopenharmony_ci	unsigned long flags;
6662306a36Sopenharmony_ci	long save_sp, save_vbr;
6762306a36Sopenharmony_ci	long tmp_vectors[3];
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	local_irq_save(flags);
7062306a36Sopenharmony_ci	__asm__ __volatile__ (
7162306a36Sopenharmony_ci		"movec %/vbr,%2\n\t"
7262306a36Sopenharmony_ci		"movel #Lberr2,%4@(8)\n\t"
7362306a36Sopenharmony_ci		"movec %4,%/vbr\n\t"
7462306a36Sopenharmony_ci		"movel %/sp,%1\n\t"
7562306a36Sopenharmony_ci		"moveq #0,%0\n\t"
7662306a36Sopenharmony_ci		"movew %5,%3@\n\t"
7762306a36Sopenharmony_ci		"nop\n\t"
7862306a36Sopenharmony_ci		/*
7962306a36Sopenharmony_ci		 * If this nop isn't present, 'ret' may already be loaded
8062306a36Sopenharmony_ci		 * with 1 at the time the bus error happens!
8162306a36Sopenharmony_ci		 */
8262306a36Sopenharmony_ci		"moveq #1,%0\n"
8362306a36Sopenharmony_ci	"Lberr2:\n\t"
8462306a36Sopenharmony_ci		"movel %1,%/sp\n\t"
8562306a36Sopenharmony_ci		"movec %2,%/vbr"
8662306a36Sopenharmony_ci		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
8762306a36Sopenharmony_ci		: "a" (regp), "a" (tmp_vectors), "g" (val)
8862306a36Sopenharmony_ci	);
8962306a36Sopenharmony_ci	local_irq_restore(flags);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL(hwreg_write);
9462306a36Sopenharmony_ci
95