1 /* $NetBSD: lcd.c,v 1.13 2023/01/15 05:08:33 tsutsui Exp $ */ 2 /* $OpenBSD: lcd.c,v 1.7 2015/02/10 22:42:35 miod Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Tohru Nishimura. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 34 35 __KERNEL_RCSID(0, "$NetBSD: lcd.c,v 1.13 2023/01/15 05:08:33 tsutsui Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/device.h> 41 #include <sys/ioctl.h> 42 #include <sys/fcntl.h> 43 #include <sys/errno.h> 44 45 #include <machine/autoconf.h> 46 #include <machine/board.h> 47 #include <machine/cpu.h> 48 #include <machine/lcd.h> 49 50 #include "ioconf.h" 51 52 #define PIO1_MODE_OUTPUT 0x84 53 #define PIO1_MODE_INPUT 0x94 54 55 #define POWER 0x10 56 57 #define ENABLE 0x80 58 #define DISABLE 0x00 59 60 #define WRITE_CMD (0x00 | 0x00) 61 #define WRITE_DATA (0x00 | 0x40) 62 #define READ_BUSY (0x20 | 0x00) 63 #define READ_DATA (0x20 | 0x40) 64 65 #define LCD_INIT 0x38 66 #define LCD_ENTRY 0x06 67 #define LCD_ON 0x0c 68 #define LCD_CLS 0x01 69 #define LCD_HOME 0x02 70 #define LCD_LOCATE(X, Y) (((Y) & 1 ? 0xc0 : 0x80) | ((X) & 0x0f)) 71 72 #define LCD_MAXBUFLEN 80 73 74 struct pio { 75 volatile uint8_t portA; 76 volatile uint8_t portB; 77 volatile uint8_t portC; 78 volatile uint8_t cntrl; 79 }; 80 81 /* Autoconf stuff */ 82 static int lcd_match(device_t, cfdata_t, void *); 83 static void lcd_attach(device_t, device_t, void *); 84 85 static dev_type_open(lcdopen); 86 static dev_type_close(lcdclose); 87 static dev_type_write(lcdwrite); 88 static dev_type_ioctl(lcdioctl); 89 90 const struct cdevsw lcd_cdevsw = { 91 .d_open = lcdopen, 92 .d_close = lcdclose, 93 .d_read = noread, 94 .d_write = lcdwrite, 95 .d_ioctl = lcdioctl, 96 .d_stop = nostop, 97 .d_tty = notty, 98 .d_poll = nopoll, 99 .d_mmap = nommap, 100 .d_kqfilter = nokqfilter, 101 .d_discard = nodiscard, 102 .d_flag = 0 103 }; 104 105 struct lcd_softc { 106 device_t sc_dev; 107 108 bool sc_opened; 109 }; 110 111 CFATTACH_DECL_NEW(lcd, sizeof(struct lcd_softc), 112 lcd_match, lcd_attach, NULL, NULL); 113 114 static void lcdbusywait(void); 115 static void lcdput(int); 116 static void lcdctrl(int); 117 static void lcdshow(char *); 118 static void greeting(void); 119 /* "1234567890123456" */ 120 static char lcd_boot_message1[] = " NetBSD/luna68k "; 121 static char lcd_boot_message2[] = " SX-9100/DT "; 122 123 /* 124 * Autoconf functions 125 */ 126 static int 127 lcd_match(device_t parent, cfdata_t cf, void *aux) 128 { 129 struct mainbus_attach_args *ma = aux; 130 131 if (strcmp(ma->ma_name, lcd_cd.cd_name)) 132 return 0; 133 if (badaddr((void *)ma->ma_addr, 4)) 134 return 0; 135 return 1; 136 } 137 138 static void 139 lcd_attach(device_t parent, device_t self, void *aux) 140 { 141 142 printf("\n"); 143 144 /* Say hello to the world on LCD. */ 145 greeting(); 146 } 147 148 /* 149 * open/close/write/ioctl 150 */ 151 static int 152 lcdopen(dev_t dev, int flags, int fmt, struct lwp *l) 153 { 154 int unit; 155 struct lcd_softc *sc; 156 157 unit = minor(dev); 158 sc = device_lookup_private(&lcd_cd, unit); 159 if (sc == NULL) 160 return ENXIO; 161 if (sc->sc_opened) 162 return EBUSY; 163 sc->sc_opened = true; 164 165 return 0; 166 } 167 168 static int 169 lcdclose(dev_t dev, int flags, int fmt, struct lwp *l) 170 { 171 int unit; 172 struct lcd_softc *sc; 173 174 unit = minor(dev); 175 sc = device_lookup_private(&lcd_cd, unit); 176 sc->sc_opened = false; 177 178 return 0; 179 } 180 181 static int 182 lcdwrite(dev_t dev, struct uio *uio, int flag) 183 { 184 int error; 185 size_t len, i; 186 char buf[LCD_MAXBUFLEN]; 187 188 len = uio->uio_resid; 189 190 if (len > LCD_MAXBUFLEN) 191 return EIO; 192 193 error = uiomove(buf, len, uio); 194 if (error) 195 return EIO; 196 197 for (i = 0; i < len; i++) { 198 lcdput((int)buf[i]); 199 } 200 201 return 0; 202 } 203 204 static int 205 lcdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 206 { 207 int val; 208 209 /* check if the device opened with write mode */ 210 switch (cmd) { 211 case LCDCLS: 212 case LCDHOME: 213 case LCDMODE: 214 case LCDDISP: 215 case LCDMOVE: 216 case LCDSEEK: 217 case LCDRESTORE: 218 if ((flag & FWRITE) == 0) 219 return EACCES; 220 } 221 222 switch (cmd) { 223 case LCDCLS: 224 lcdctrl(LCD_CLS); 225 break; 226 227 case LCDHOME: 228 lcdctrl(LCD_HOME); 229 break; 230 231 case LCDMODE: 232 val = *(int *)addr; 233 switch (val) { 234 case LCDMODE_C_LEFT: 235 case LCDMODE_C_RIGHT: 236 case LCDMODE_D_LEFT: 237 case LCDMODE_D_RIGHT: 238 lcdctrl(val); 239 break; 240 default: 241 return EINVAL; 242 } 243 break; 244 245 case LCDDISP: 246 val = *(int *)addr; 247 if ((val & 0x7) != val) 248 return EINVAL; 249 lcdctrl(val | 0x8); 250 break; 251 252 case LCDMOVE: 253 val = *(int *)addr; 254 switch (val) { 255 case LCDMOVE_C_LEFT: 256 case LCDMOVE_C_RIGHT: 257 case LCDMOVE_D_LEFT: 258 case LCDMOVE_D_RIGHT: 259 lcdctrl(val); 260 break; 261 default: 262 return EINVAL; 263 } 264 break; 265 266 case LCDSEEK: 267 val = *(int *)addr & 0x7f; 268 lcdctrl(val | 0x80); 269 break; 270 271 case LCDRESTORE: 272 greeting(); 273 break; 274 275 default: 276 return ENOTTY; 277 } 278 return EPASSTHROUGH; 279 } 280 281 static void 282 lcdbusywait(void) 283 { 284 struct pio *p1 = (struct pio *)OBIO_PIO1_BASE; 285 int msb, s; 286 287 s = splhigh(); 288 p1->cntrl = PIO1_MODE_INPUT; 289 p1->portC = POWER | READ_BUSY | ENABLE; 290 splx(s); 291 292 do { 293 msb = p1->portA & ENABLE; 294 delay(5); 295 } while (msb != 0); 296 297 s = splhigh(); 298 p1->portC = POWER | READ_BUSY | DISABLE; 299 splx(s); 300 } 301 302 static void 303 lcdput(int cc) 304 { 305 struct pio *p1 = (struct pio *)OBIO_PIO1_BASE; 306 int s; 307 308 lcdbusywait(); 309 310 s = splhigh(); 311 p1->cntrl = PIO1_MODE_OUTPUT; 312 313 p1->portC = POWER | WRITE_DATA | ENABLE; 314 p1->portA = cc; 315 p1->portC = POWER | WRITE_DATA | DISABLE; 316 splx(s); 317 } 318 319 static void 320 lcdctrl(int cc) 321 { 322 struct pio *p1 = (struct pio *)OBIO_PIO1_BASE; 323 int s; 324 325 lcdbusywait(); 326 327 s = splhigh(); 328 p1->cntrl = PIO1_MODE_OUTPUT; 329 330 p1->portC = POWER | WRITE_CMD | ENABLE; 331 p1->portA = cc; 332 p1->portC = POWER | WRITE_CMD | DISABLE; 333 splx(s); 334 } 335 336 static void 337 lcdshow(char *s) 338 { 339 int cc; 340 341 while ((cc = *s++) != '\0') 342 lcdput(cc); 343 } 344 345 static void 346 greeting(void) 347 { 348 349 lcdctrl(LCD_INIT); 350 lcdctrl(LCD_ENTRY); 351 lcdctrl(LCD_ON); 352 353 lcdctrl(LCD_CLS); 354 lcdctrl(LCD_HOME); 355 356 lcdctrl(LCD_LOCATE(0, 0)); 357 lcdshow(lcd_boot_message1); 358 lcdctrl(LCD_LOCATE(0, 1)); 359 if (machtype == LUNA_II) 360 lcd_boot_message2[13] = '2'; 361 lcdshow(lcd_boot_message2); 362 } 363