1 /* $NetBSD: lcd.c,v 1.8 2017/03/09 14:05:58 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.8 2017/03/09 14:05:58 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/cpu.h> 47 #include <machine/lcd.h> 48 49 #include "ioconf.h" 50 51 #define PIO1_MODE_OUTPUT 0x84 52 #define PIO1_MODE_INPUT 0x94 53 54 #define POWER 0x10 55 56 #define ENABLE 0x80 57 #define DISABLE 0x00 58 59 #define WRITE_CMD (0x00 | 0x00) 60 #define WRITE_DATA (0x00 | 0x40) 61 #define READ_BUSY (0x20 | 0x00) 62 #define READ_DATA (0x20 | 0x40) 63 64 #define LCD_INIT 0x38 65 #define LCD_ENTRY 0x06 66 #define LCD_ON 0x0c 67 #define LCD_CLS 0x01 68 #define LCD_HOME 0x02 69 #define LCD_LOCATE(X, Y) (((Y) & 1 ? 0xc0 : 0x80) | ((X) & 0x0f)) 70 71 #define LCD_MAXBUFLEN 80 72 73 struct pio { 74 volatile u_int8_t portA; 75 volatile u_int8_t portB; 76 volatile u_int8_t portC; 77 volatile u_int8_t cntrl; 78 }; 79 80 /* Autoconf stuff */ 81 static int lcd_match(device_t, cfdata_t, void *); 82 static void lcd_attach(device_t, device_t, void *); 83 84 dev_type_open(lcdopen); 85 dev_type_close(lcdclose); 86 dev_type_write(lcdwrite); 87 dev_type_ioctl(lcdioctl); 88 89 const struct cdevsw lcd_cdevsw = { 90 .d_open = lcdopen, 91 .d_close = lcdclose, 92 .d_read = noread, 93 .d_write = lcdwrite, 94 .d_ioctl = lcdioctl, 95 .d_stop = nostop, 96 .d_tty = notty, 97 .d_poll = nopoll, 98 .d_mmap = nommap, 99 .d_kqfilter = nokqfilter, 100 .d_discard = nodiscard, 101 .d_flag = 0 102 }; 103 104 struct lcd_softc { 105 device_t sc_dev; 106 107 bool sc_opened; 108 }; 109 110 CFATTACH_DECL_NEW(lcd, sizeof(struct lcd_softc), 111 lcd_match, lcd_attach, NULL, NULL); 112 113 void lcdbusywait(void); 114 void lcdput(int); 115 void lcdctrl(int); 116 void lcdshow(char *); 117 void greeting(void); 118 /* "1234567890123456" */ 119 static char lcd_boot_message1[] = " NetBSD/luna68k "; 120 static char lcd_boot_message2[] = " SX-9100/DT "; 121 122 /* 123 * Autoconf functions 124 */ 125 static int 126 lcd_match(device_t parent, cfdata_t cf, void *aux) 127 { 128 struct mainbus_attach_args *ma = aux; 129 130 if (strcmp(ma->ma_name, lcd_cd.cd_name)) 131 return 0; 132 if (badaddr((void *)ma->ma_addr, 4)) 133 return 0; 134 return 1; 135 } 136 137 static void 138 lcd_attach(device_t parent, device_t self, void *aux) 139 { 140 141 printf("\n"); 142 143 /* Say hello to the world on LCD. */ 144 greeting(); 145 } 146 147 /* 148 * open/close/write/ioctl 149 */ 150 int 151 lcdopen(dev_t dev, int flags, int fmt, struct lwp *l) 152 { 153 int unit; 154 struct lcd_softc *sc; 155 156 unit = minor(dev); 157 sc = device_lookup_private(&lcd_cd, unit); 158 if (sc == NULL) 159 return ENXIO; 160 if (sc->sc_opened) 161 return EBUSY; 162 sc->sc_opened = true; 163 164 return 0; 165 } 166 167 int 168 lcdclose(dev_t dev, int flags, int fmt, struct lwp *l) 169 { 170 int unit; 171 struct lcd_softc *sc; 172 173 unit = minor(dev); 174 sc = device_lookup_private(&lcd_cd, unit); 175 sc->sc_opened = false; 176 177 return 0; 178 } 179 180 int 181 lcdwrite(dev_t dev, struct uio *uio, int flag) 182 { 183 int error; 184 size_t len, i; 185 char buf[LCD_MAXBUFLEN]; 186 187 len = uio->uio_resid; 188 189 if (len > LCD_MAXBUFLEN) 190 return EIO; 191 192 error = uiomove(buf, len, uio); 193 if (error) 194 return EIO; 195 196 for (i = 0; i < len; i++) { 197 lcdput((int)buf[i]); 198 } 199 200 return 0; 201 } 202 203 int 204 lcdioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 205 { 206 int val; 207 208 /* check if the device opened with write mode */ 209 switch (cmd) { 210 case LCDCLS: 211 case LCDHOME: 212 case LCDMODE: 213 case LCDDISP: 214 case LCDMOVE: 215 case LCDSEEK: 216 case LCDRESTORE: 217 if ((flag & FWRITE) == 0) 218 return EACCES; 219 break; 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 void 282 lcdbusywait(void) 283 { 284 struct pio *p1 = (struct pio *)0x4D000000; 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 void 303 lcdput(int cc) 304 { 305 struct pio *p1 = (struct pio *)0x4D000000; 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 void 320 lcdctrl(int cc) 321 { 322 struct pio *p1 = (struct pio *)0x4D000000; 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 void 337 lcdshow(char *s) 338 { 339 int cc; 340 341 while ((cc = *s++) != '\0') 342 lcdput(cc); 343 } 344 345 void 346 greeting(void) 347 { 348 lcdctrl(LCD_INIT); 349 lcdctrl(LCD_ENTRY); 350 lcdctrl(LCD_ON); 351 352 lcdctrl(LCD_CLS); 353 lcdctrl(LCD_HOME); 354 355 lcdctrl(LCD_LOCATE(0, 0)); 356 lcdshow(lcd_boot_message1); 357 lcdctrl(LCD_LOCATE(0, 1)); 358 if (machtype == LUNA_II) 359 lcd_boot_message2[13] = '2'; 360 lcdshow(lcd_boot_message2); 361 } 362