1/*	$NetBSD: switch.c,v 1.1 2014/08/05 13:49:04 isaki Exp $	*/
2
3/*
4 * Copyright (c) 2014 Tetsuya Isaki. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <lib/libsa/stand.h>
30#include <lib/libkern/libkern.h>
31
32#include "switch.h"
33
34#define SRAM_MEMSIZE	(*((volatile uint32_t *)0x00ed0008))
35#define SRAM_ROMADDR	(*((volatile uint32_t *)0x00ed000c))
36#define SRAM_RAMADDR	(*((volatile uint32_t *)0x00ed0010))
37#define SRAM_BOOTDEV	(*((volatile uint16_t *)0x00ed0018))
38
39#define SYSPORT_SRAM_WP	(*((volatile uint8_t *)0x00e8e00d))
40
41static int hextoi(const char *);
42static void cmd_switch_help(void);
43static void cmd_switch_show(void);
44static void cmd_switch_show_boot(void);
45static void cmd_switch_show_rom(void);
46static void cmd_switch_show_memory(void);
47static const char *romaddr_tostr(uint32_t);
48static const char *get_romaddr_name(uint32_t);
49static void cmd_switch_boot(const char *);
50static void cmd_switch_rom(const char *);
51static void cmd_switch_memory(const char *);
52
53static inline void
54sram_write_enable(void)
55{
56	SYSPORT_SRAM_WP = 0x31;
57}
58
59static inline void
60sram_write_disable(void)
61{
62	SYSPORT_SRAM_WP = 0;
63}
64
65static int
66hextoi(const char *in)
67{
68	char *c;
69	int ret;
70
71	ret = 0;
72	c = (char *)in;
73	for (; isxdigit(*c); c++) {
74		ret = (ret * 16) +
75		      (*c > '9' ? ((*c | 0x20) - 'a' + 10) : *c - '0');
76	}
77	return ret;
78}
79
80static void
81cmd_switch_help(void)
82{
83	printf(
84		"usage: switch <key>=<val>\n"
85		"         boot=[std | inscsi<N> | exscsi<N> | fd<N> | rom ]\n"
86		"         rom=[ inscsi<N> | exscsi<N> | $<addr> ]\n"
87		"         memory=<1..12> (unit:MB)\n"
88		"       switch show\n"
89	);
90}
91
92void
93cmd_switch(char *arg)
94{
95	char *val;
96
97	if (strcmp(arg, "show") == 0) {
98		cmd_switch_show();
99		return;
100	}
101
102	val = strchr(arg, '=');
103	if (val == NULL) {
104		cmd_switch_help();
105		return;
106	}
107	*val++ = '\0';
108
109	if (strcmp(arg, "boot") == 0) {
110		cmd_switch_boot(val);
111	} else if (strcmp(arg, "rom") == 0) {
112		cmd_switch_rom(val);
113	} else if (strcmp(arg, "memory") == 0) {
114		cmd_switch_memory(val);
115	} else {
116		cmd_switch_help();
117	}
118}
119
120static void
121cmd_switch_show(void)
122{
123	cmd_switch_show_boot();
124	cmd_switch_show_rom();
125	cmd_switch_show_memory();
126}
127
128static void
129cmd_switch_show_boot(void)
130{
131	uint32_t romaddr;
132	uint16_t bootdev;
133	const char *name;
134
135	bootdev = SRAM_BOOTDEV;
136	romaddr = SRAM_ROMADDR;
137
138	/*
139	 * $0000: std
140	 * $8n00: sasi<N>
141	 * $9n70: fd<N>
142	 * $a000: ROM
143	 * $b000: RAM
144	 */
145	printf("boot=");
146	switch (bootdev >> 12) {
147	default:
148	case 0x0:
149		/*
150		 * The real order is fd->sasi->rom->ram
151		 * but it is a bit redundant..
152		 */
153		printf("std (fd -> ");
154		name = get_romaddr_name(romaddr);
155		if (name)
156			printf("%s)", name);
157		else
158			printf("rom$%x)", romaddr);
159		break;
160	case 0x8:
161		printf("sasi%d", (bootdev >> 8) & 15);
162		break;
163	case 0x9:
164		printf("fd%d", (bootdev >> 8) & 3);
165		break;
166	case 0xa:
167		printf("rom%s", romaddr_tostr(romaddr));
168		break;
169	case 0xb:
170		printf("ram$%x", SRAM_RAMADDR);
171		break;
172	}
173	printf("\n");
174}
175
176static void
177cmd_switch_show_rom(void)
178{
179	uint32_t romaddr;
180
181	romaddr = SRAM_ROMADDR;
182	printf("rom=%s\n", romaddr_tostr(romaddr));
183}
184
185static void
186cmd_switch_show_memory(void)
187{
188	printf("memory=%d MB\n", SRAM_MEMSIZE / (1024 * 1024));
189}
190
191/* return rom address as string with name if any */
192static const char *
193romaddr_tostr(uint32_t addr)
194{
195	static char buf[32];
196	const char *name;
197
198	name = get_romaddr_name(addr);
199	if (name)
200		snprintf(buf, sizeof(buf), "$%x (%s)", addr, name);
201	else
202		snprintf(buf, sizeof(buf), "$%x", addr);
203
204	return buf;
205}
206
207/*
208 * return "inscsiN" / "exscsiN" if addr is in range of SCSI boot.
209 * Otherwise return NULL.
210 */
211static const char *
212get_romaddr_name(uint32_t addr)
213{
214	static char buf[8];
215
216	if (0xfc0000 <= addr && addr < 0xfc0020 && addr % 4 == 0) {
217		snprintf(buf, sizeof(buf), "inscsi%d", (addr >> 2) & 7);
218	} else if (0xea0020 <= addr && addr < 0xea0040 && addr % 4 == 0) {
219		snprintf(buf, sizeof(buf), "exscsi%d", (addr >> 2) & 7);
220	} else {
221		return NULL;
222	}
223	return buf;
224}
225
226static void
227cmd_switch_boot(const char *arg)
228{
229	int id;
230	uint32_t romaddr;
231	uint16_t bootdev;
232
233	romaddr = 0xffffffff;
234
235	if (strcmp(arg, "std") == 0) {
236		bootdev = 0x0000;
237
238	} else if (strcmp(arg, "rom") == 0) {
239		bootdev = 0xa000;
240
241	} else if (strncmp(arg, "inscsi", 6) == 0) {
242		id = (arg[6] - '0') & 7;
243		bootdev = 0xa000;
244		romaddr = 0xfc0000 + id * 4;
245
246	} else if (strncmp(arg, "exscsi", 6) == 0) {
247		id = (arg[6] - '0') & 7;
248		bootdev = 0xa000;
249		romaddr = 0xea0020 + id * 4;
250
251	} else if (strncmp(arg, "fd", 2) == 0) {
252		id = (arg[2] - '0') & 3;
253		bootdev = 0x9070 | (id << 8);
254
255	} else {
256		cmd_switch_help();
257		return;
258	}
259
260	sram_write_enable();
261	SRAM_BOOTDEV = bootdev;
262	if (romaddr != 0xffffffff)
263		SRAM_ROMADDR = romaddr;
264	sram_write_disable();
265
266	cmd_switch_show_boot();
267}
268
269static void
270cmd_switch_rom(const char *arg)
271{
272	int id;
273	uint32_t romaddr;
274
275	if (strncmp(arg, "inscsi", 6) == 0) {
276		id = (arg[6] - '0') & 7;
277		romaddr = 0xfc0000 + id * 4;
278
279	} else if (strncmp(arg, "exscsi", 6) == 0) {
280		id = (arg[6] - '0') & 7;
281		romaddr = 0xea0020 + id * 4;
282
283	} else if (*arg == '$') {
284		romaddr = hextoi(arg + 1);
285
286	} else {
287		cmd_switch_help();
288		return;
289	}
290
291	sram_write_enable();
292	SRAM_ROMADDR = romaddr;
293	sram_write_disable();
294
295	cmd_switch_show_rom();
296}
297
298static void
299cmd_switch_memory(const char *arg)
300{
301	int num;
302
303	num = atoi(arg);
304	if (num < 1 || num > 12) {
305		cmd_switch_help();
306		return;
307	}
308
309	sram_write_enable();
310	SRAM_MEMSIZE = num * (1024 * 1024);
311	sram_write_disable();
312
313	cmd_switch_show_memory();
314}
315