18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Tests for presence or absence of hardware registers.
38c2ecf20Sopenharmony_ci * This code was originally in atari/config.c, but I noticed
48c2ecf20Sopenharmony_ci * that it was also in drivers/nubus/nubus.c and I wanted to
58c2ecf20Sopenharmony_ci * use it in hp300/config.c, so it seemed sensible to pull it
68c2ecf20Sopenharmony_ci * out into its own file.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * The test is for use when trying to read a hardware register
98c2ecf20Sopenharmony_ci * that isn't present would cause a bus error. We set up a
108c2ecf20Sopenharmony_ci * temporary handler so that this doesn't kill the kernel.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * There is a test-by-reading and a test-by-writing; I present
138c2ecf20Sopenharmony_ci * them here complete with the comments from the original atari
148c2ecf20Sopenharmony_ci * config.c...
158c2ecf20Sopenharmony_ci *                -- PMM <pmaydell@chiark.greenend.org.uk>, 05/1998
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* This function tests for the presence of an address, specially a
198c2ecf20Sopenharmony_ci * hardware register address. It is called very early in the kernel
208c2ecf20Sopenharmony_ci * initialization process, when the VBR register isn't set up yet. On
218c2ecf20Sopenharmony_ci * an Atari, it still points to address 0, which is unmapped. So a bus
228c2ecf20Sopenharmony_ci * error would cause another bus error while fetching the exception
238c2ecf20Sopenharmony_ci * vector, and the CPU would do nothing at all. So we needed to set up
248c2ecf20Sopenharmony_ci * a temporary VBR and a vector table for the duration of the test.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint hwreg_present(volatile void *regp)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	int ret = 0;
328c2ecf20Sopenharmony_ci	unsigned long flags;
338c2ecf20Sopenharmony_ci	long save_sp, save_vbr;
348c2ecf20Sopenharmony_ci	long tmp_vectors[3];
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	local_irq_save(flags);
378c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
388c2ecf20Sopenharmony_ci		"movec %/vbr,%2\n\t"
398c2ecf20Sopenharmony_ci		"movel #Lberr1,%4@(8)\n\t"
408c2ecf20Sopenharmony_ci		"movec %4,%/vbr\n\t"
418c2ecf20Sopenharmony_ci		"movel %/sp,%1\n\t"
428c2ecf20Sopenharmony_ci		"moveq #0,%0\n\t"
438c2ecf20Sopenharmony_ci		"tstb %3@\n\t"
448c2ecf20Sopenharmony_ci		"nop\n\t"
458c2ecf20Sopenharmony_ci		"moveq #1,%0\n"
468c2ecf20Sopenharmony_ci	"Lberr1:\n\t"
478c2ecf20Sopenharmony_ci		"movel %1,%/sp\n\t"
488c2ecf20Sopenharmony_ci		"movec %2,%/vbr"
498c2ecf20Sopenharmony_ci		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
508c2ecf20Sopenharmony_ci		: "a" (regp), "a" (tmp_vectors)
518c2ecf20Sopenharmony_ci	);
528c2ecf20Sopenharmony_ci	local_irq_restore(flags);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return ret;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hwreg_present);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Basically the same, but writes a value into a word register, protected
598c2ecf20Sopenharmony_ci * by a bus error handler. Returns 1 if successful, 0 otherwise.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciint hwreg_write(volatile void *regp, unsigned short val)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int ret;
658c2ecf20Sopenharmony_ci	unsigned long flags;
668c2ecf20Sopenharmony_ci	long save_sp, save_vbr;
678c2ecf20Sopenharmony_ci	long tmp_vectors[3];
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	local_irq_save(flags);
708c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
718c2ecf20Sopenharmony_ci		"movec %/vbr,%2\n\t"
728c2ecf20Sopenharmony_ci		"movel #Lberr2,%4@(8)\n\t"
738c2ecf20Sopenharmony_ci		"movec %4,%/vbr\n\t"
748c2ecf20Sopenharmony_ci		"movel %/sp,%1\n\t"
758c2ecf20Sopenharmony_ci		"moveq #0,%0\n\t"
768c2ecf20Sopenharmony_ci		"movew %5,%3@\n\t"
778c2ecf20Sopenharmony_ci		"nop\n\t"
788c2ecf20Sopenharmony_ci		/*
798c2ecf20Sopenharmony_ci		 * If this nop isn't present, 'ret' may already be loaded
808c2ecf20Sopenharmony_ci		 * with 1 at the time the bus error happens!
818c2ecf20Sopenharmony_ci		 */
828c2ecf20Sopenharmony_ci		"moveq #1,%0\n"
838c2ecf20Sopenharmony_ci	"Lberr2:\n\t"
848c2ecf20Sopenharmony_ci		"movel %1,%/sp\n\t"
858c2ecf20Sopenharmony_ci		"movec %2,%/vbr"
868c2ecf20Sopenharmony_ci		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
878c2ecf20Sopenharmony_ci		: "a" (regp), "a" (tmp_vectors), "g" (val)
888c2ecf20Sopenharmony_ci	);
898c2ecf20Sopenharmony_ci	local_irq_restore(flags);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return ret;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hwreg_write);
948c2ecf20Sopenharmony_ci
95