1/* $NetBSD: gecko.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <lib/libsa/stand.h>
30
31#include <machine/pio.h>
32
33#include "gecko.h"
34#include "timer.h"
35
36#define EXI_BASE		0x0d806800
37#define	EXI_CSR(n)		(EXI_BASE + 0x00 + (n) * 0x14)
38#define  EXI_CSR_EXTINT		__BIT(11)
39#define  EXI_CSR_EXTINTMASK	__BIT(10)
40#define	 EXI_CSR_CS		__BITS(9,7)
41#define	 EXI_CSR_CLK		__BITS(6,4)
42#define	EXI_CR(n)		(EXI_BASE + 0x0c + (n) * 0x14)
43#define	 EXI_CR_TLEN		__BITS(5,4)
44#define  EXI_CR_RW		__BITS(3,2)
45#define  EXI_CR_RW_READ		__SHIFTIN(0, EXI_CR_RW)
46#define  EXI_CR_RW_WRITE	__SHIFTIN(1, EXI_CR_RW)
47#define  EXI_CR_RW_READWRITE	__SHIFTIN(2, EXI_CR_RW)
48#define	 EXI_CR_DMA		__BIT(1)
49#define  EXI_CR_TSTART		__BIT(0)
50#define	EXI_DATA(n)		(EXI_BASE + 0x10 + (n) * 0x14)
51
52#define SI_BASE			0x0d806400
53#define SI_EXILK		(SI_BASE + 0x03c)
54
55#define	EXI_FREQ_32MHZ		5
56
57#define GECKO_CSR					\
58	(__SHIFTIN(1, EXI_CSR_CS) |			\
59	 __SHIFTIN(EXI_FREQ_32MHZ, EXI_CSR_CLK))
60#define GECKO_CR(len, rdwr)				\
61	(__SHIFTIN((len) - 1, EXI_CR_TLEN) | 		\
62	 (rdwr) | EXI_CR_TSTART)
63
64#define GECKO_CMD_ID		0x9000
65#define GECKO_CMD_RECV_BYTE	0xa000
66#define GECKO_CMD_SEND_BYTE	0xb000
67
68static int gecko_chan = -1;
69static int gecko_keycurrent;
70static int gecko_keypending;
71
72static void
73gecko_wait(void)
74{
75	int retry;
76
77	for (retry = 0; retry < 1000; retry++) {
78		if ((in32(EXI_CR(gecko_chan)) & EXI_CR_TSTART) == 0) {
79			return;
80		}
81	}
82}
83
84static uint16_t
85gecko_command(uint16_t command)
86{
87	uint16_t value;
88
89	out32(EXI_CSR(gecko_chan), GECKO_CSR);
90	out32(EXI_DATA(gecko_chan), (uint32_t)command << 16);
91	out32(EXI_CR(gecko_chan), GECKO_CR(2, EXI_CR_RW_READWRITE));
92	gecko_wait();
93	value = in32(EXI_DATA(gecko_chan)) >> 16;
94	out32(EXI_CSR(gecko_chan), 0);
95
96	return value;
97}
98
99static uint32_t
100gecko_id(void)
101{
102	uint32_t value;
103
104	out32(EXI_CSR(gecko_chan), GECKO_CSR);
105	out32(EXI_DATA(gecko_chan), 0);
106	out32(EXI_CR(gecko_chan), GECKO_CR(2, EXI_CR_RW_READWRITE));
107	gecko_wait();
108	out32(EXI_CR(gecko_chan), GECKO_CR(4, EXI_CR_RW_READWRITE));
109	gecko_wait();
110	value = in32(EXI_DATA(gecko_chan));
111	out32(EXI_CSR(gecko_chan), 0);
112
113	return value;
114}
115
116int
117gecko_getchar(void)
118{
119	int key;
120
121	if (gecko_chan == -1) {
122		return -1;
123	}
124
125	if (gecko_keypending) {
126		key = gecko_keycurrent;
127		gecko_keypending = 0;
128	} else {
129		uint16_t value;
130
131		do {
132			value = gecko_command(GECKO_CMD_RECV_BYTE);
133		} while ((value & 0x0800) == 0);
134		key = value & 0xff;
135	}
136
137	return key;
138}
139
140void
141gecko_putchar(int c)
142{
143	if (gecko_chan == -1) {
144		return;
145	}
146
147	c &= 0xff;
148	gecko_command(GECKO_CMD_SEND_BYTE | (c << 4));
149}
150
151int
152gecko_ischar(void)
153{
154	if (gecko_chan != -1 && !gecko_keypending) {
155		uint16_t value;
156
157		value = gecko_command(GECKO_CMD_RECV_BYTE);
158		if ((value & 0x0800) != 0) {
159			gecko_keycurrent = value & 0xff;
160			gecko_keypending = 1;
161		}
162	}
163
164	return gecko_keypending;
165}
166
167static int
168gecko_detect(void)
169{
170	int nsamples = 0;
171
172	/*
173	 * MINI may be still using EXI when we get here. Wait for it to go
174	 * idle before probing for Gecko.
175	 */
176	while (nsamples++ < 1000) {
177		if ((in32(EXI_CR(gecko_chan)) & EXI_CR_TSTART) != 0) {
178			printf("TSTART set, resetting samples\n");
179			nsamples = 0;
180		}
181		timer_udelay(1000);
182	}
183
184	out32(EXI_CSR(gecko_chan), 0);
185	out32(EXI_CSR(gecko_chan), EXI_CSR_EXTINT | EXI_CSR_EXTINTMASK);
186
187	return gecko_id() == 0 &&
188	       gecko_command(GECKO_CMD_ID) == 0x0470;
189}
190
191int
192gecko_probe(void)
193{
194	out32(SI_EXILK, 0);
195
196	gecko_chan = 0;
197	if (!gecko_detect()) {
198		gecko_chan = 1;
199		if (!gecko_detect()) {
200			gecko_chan = -1;
201		}
202	}
203
204	return gecko_chan;
205}
206