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