1 /* $NetBSD: lcdpanel.c,v 1.1 2018/04/09 20:16:16 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Dennis I. Chernoivanov 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: lcdpanel.c,v 1.1 2018/04/09 20:16:16 christos Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/proc.h> 36 #include <sys/poll.h> 37 #include <sys/conf.h> 38 #include <sys/uio.h> 39 #include <sys/types.h> 40 #include <sys/kernel.h> 41 #include <sys/device.h> 42 #include <sys/callout.h> 43 #include <sys/select.h> 44 #include <sys/reboot.h> 45 46 #include <sys/bus.h> 47 #include <machine/autoconf.h> 48 49 #include <dev/ic/hd44780reg.h> 50 #include <dev/ic/hd44780var.h> 51 #include <dev/ic/lcdkp_subr.h> 52 53 #include "ioconf.h" 54 55 #define LCDPANEL_POLLRATE (hz / 10) 56 #define LCDPANEL_REGION 0x20 57 #define DATA_OFFSET 0x10 58 #define LCDPANEL_COLS 16 59 #define LCDPANEL_VCOLS 40 60 #define LCDPANEL_ROWS 2 61 62 struct lcdpanel_softc { 63 device_t sc_dev; 64 65 struct hd44780_chip sc_lcd; 66 struct lcdkp_chip sc_kp; 67 68 struct selinfo sc_selq; 69 struct callout sc_callout; 70 }; 71 72 struct lcd_message { 73 const char firstcol[LCDPANEL_VCOLS]; 74 const char secondcol[LCDPANEL_VCOLS]; 75 }; 76 static const struct lcd_message startup_message = { 77 "NetBSD/cobalt ", 78 "Starting up... " 79 }; 80 static const struct lcd_message halt_message = { 81 "NetBSD/cobalt ", 82 "Halting... " 83 }; 84 static const struct lcd_message reboot_message = { 85 "NetBSD/cobalt ", 86 "Rebooting... " 87 }; 88 89 static int lcdpanel_match(device_t, cfdata_t, void *); 90 static void lcdpanel_attach(device_t, device_t, void *); 91 static bool lcdpanel_shutdown(device_t, int); 92 93 static void lcdpanel_soft(void *); 94 95 static uint8_t lcdpanel_cbt_kprread(bus_space_tag_t, bus_space_handle_t); 96 static uint8_t lcdpanel_cbt_hdreadreg(struct hd44780_chip *, uint32_t, uint32_t); 97 static void lcdpanel_cbt_hdwritereg(struct hd44780_chip *, uint32_t, uint32_t, 98 uint8_t); 99 100 dev_type_open(lcdpanelopen); 101 dev_type_close(lcdpanelclose); 102 dev_type_read(lcdpanelread); 103 dev_type_write(lcdpanelwrite); 104 dev_type_ioctl(lcdpanelioctl); 105 dev_type_poll(lcdpanelpoll); 106 107 const struct cdevsw lcdpanel_cdevsw = { 108 .d_open = lcdpanelopen, 109 .d_close = lcdpanelclose, 110 .d_read = lcdpanelread, 111 .d_write = lcdpanelwrite, 112 .d_ioctl = lcdpanelioctl, 113 .d_stop = nostop, 114 .d_tty = notty, 115 .d_poll = lcdpanelpoll, 116 .d_mmap = nommap, 117 .d_kqfilter = nokqfilter, 118 .d_discard = nodiscard, 119 .d_flag = 0 120 }; 121 122 CFATTACH_DECL_NEW(lcdpanel, sizeof(struct lcdpanel_softc), 123 lcdpanel_match, lcdpanel_attach, NULL, NULL); 124 125 static int 126 lcdpanel_match(device_t parent, cfdata_t cf, void *aux) 127 { 128 129 return 1; 130 } 131 132 static void 133 lcdpanel_attach(device_t parent, device_t self, void *aux) 134 { 135 struct lcdpanel_softc *sc = device_private(self); 136 struct mainbus_attach_args *maa = aux; 137 struct hd44780_io io; 138 static struct lcdkp_xlate keys[] = { 139 { 0xfa, 'h' }, 140 { 0xf6, 'k' }, 141 { 0xde, 'l' }, 142 { 0xee, 'j' }, 143 { 0x7e, 's' }, 144 { 0xbe, 'e' } 145 }; 146 147 sc->sc_lcd.sc_dev = self; 148 sc->sc_lcd.sc_iot = maa->ma_iot; 149 if (bus_space_map(sc->sc_lcd.sc_iot, maa->ma_addr, LCDPANEL_REGION, 150 0, &sc->sc_lcd.sc_ioir)) { 151 aprint_error(": unable to map registers\n"); 152 return; 153 } 154 bus_space_subregion(sc->sc_lcd.sc_iot, sc->sc_lcd.sc_ioir, DATA_OFFSET, 155 1, &sc->sc_lcd.sc_iodr); 156 157 printf("\n"); 158 159 sc->sc_lcd.sc_dev_ok = 1; 160 sc->sc_lcd.sc_cols = LCDPANEL_COLS; 161 sc->sc_lcd.sc_vcols = LCDPANEL_VCOLS; 162 sc->sc_lcd.sc_flags = HD_8BIT | HD_MULTILINE | HD_KEYPAD; 163 164 sc->sc_lcd.sc_writereg = lcdpanel_cbt_hdwritereg; 165 sc->sc_lcd.sc_readreg = lcdpanel_cbt_hdreadreg; 166 167 hd44780_attach_subr(&sc->sc_lcd); 168 169 /* Hello World */ 170 io.dat = 0; 171 io.len = LCDPANEL_VCOLS * LCDPANEL_ROWS; 172 memcpy(io.buf, &startup_message, io.len); 173 hd44780_ddram_io(&sc->sc_lcd, sc->sc_lcd.sc_curchip, &io, 174 HD_DDRAM_WRITE); 175 176 pmf_device_register1(self, NULL, NULL, lcdpanel_shutdown); 177 178 sc->sc_kp.sc_iot = maa->ma_iot; 179 sc->sc_kp.sc_ioh = MIPS_PHYS_TO_KSEG1(LCDPANEL_BASE); /* XXX */ 180 181 sc->sc_kp.sc_knum = sizeof(keys) / sizeof(struct lcdkp_xlate); 182 sc->sc_kp.sc_kpad = keys; 183 sc->sc_kp.sc_rread = lcdpanel_cbt_kprread; 184 185 lcdkp_attach_subr(&sc->sc_kp); 186 187 callout_init(&sc->sc_callout, 0); 188 selinit(&sc->sc_selq); 189 } 190 191 static bool 192 lcdpanel_shutdown(device_t self, int howto) 193 { 194 struct lcdpanel_softc *sc = device_private(self); 195 struct hd44780_io io; 196 197 /* Goodbye World */ 198 io.dat = 0; 199 io.len = LCDPANEL_VCOLS * LCDPANEL_ROWS; 200 if (howto & RB_HALT) 201 memcpy(io.buf, &halt_message, io.len); 202 else 203 memcpy(io.buf, &reboot_message, io.len); 204 hd44780_ddram_io(&sc->sc_lcd, sc->sc_lcd.sc_curchip, &io, 205 HD_DDRAM_WRITE); 206 207 return true; 208 } 209 210 static uint8_t 211 lcdpanel_cbt_kprread(bus_space_tag_t iot, bus_space_handle_t ioh) 212 { 213 214 delay(HD_TIMEOUT_NORMAL); 215 return (bus_space_read_4(iot, ioh, 0x00) >> 24) & 0xff; 216 } 217 218 219 static void 220 lcdpanel_cbt_hdwritereg(struct hd44780_chip *hd, uint32_t en, uint32_t rs, 221 uint8_t dat) 222 { 223 224 if (rs) 225 bus_space_write_4(hd->sc_iot, hd->sc_iodr, 0x00, dat << 24); 226 else 227 bus_space_write_4(hd->sc_iot, hd->sc_ioir, 0x00, dat << 24); 228 delay(HD_TIMEOUT_NORMAL); 229 } 230 231 static uint8_t 232 lcdpanel_cbt_hdreadreg(struct hd44780_chip *hd, uint32_t en, uint32_t rs) 233 { 234 235 delay(HD_TIMEOUT_NORMAL); 236 if (rs) 237 return (bus_space_read_4(hd->sc_iot, hd->sc_iodr, 0x00) >> 24) 238 & 0xff; 239 else 240 return (bus_space_read_4(hd->sc_iot, hd->sc_ioir, 0x00) >> 24) 241 & 0xff; 242 } 243 244 int 245 lcdpanelopen(dev_t dev, int flag, int mode, struct lwp *l) 246 { 247 struct lcdpanel_softc *sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 248 249 return (sc->sc_lcd.sc_dev_ok == 0) ? ENXIO : 0; 250 } 251 252 int 253 lcdpanelclose(dev_t dev, int flag, int mode, struct lwp *l) 254 { 255 struct lcdpanel_softc *sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 256 257 selnotify(&sc->sc_selq, 0, 0); 258 return 0; 259 } 260 261 int 262 lcdpanelread(dev_t dev, struct uio *uio, int flag) 263 { 264 int error; 265 uint8_t b; 266 struct lcdpanel_softc *sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 267 268 if (uio->uio_resid < sizeof(b)) 269 return EIO; 270 271 if ((error = lcdkp_readkey(&sc->sc_kp, &b)) != 0) 272 return error; 273 274 return uiomove((void*)&b, sizeof(b), uio); 275 } 276 277 int 278 lcdpanelwrite(dev_t dev, struct uio *uio, int flag) 279 { 280 int error; 281 struct hd44780_io io; 282 struct lcdpanel_softc *sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 283 284 io.dat = 0; 285 io.len = uio->uio_resid; 286 if (io.len > HD_MAX_CHARS) 287 io.len = HD_MAX_CHARS; 288 289 if ((error = uiomove((void*)io.buf, io.len, uio)) != 0) 290 return error; 291 292 hd44780_ddram_redraw(&sc->sc_lcd, 0, &io); 293 return 0; 294 } 295 296 int 297 lcdpanelioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 298 { 299 struct lcdpanel_softc *sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 300 301 return hd44780_ioctl_subr(&sc->sc_lcd, cmd, data); 302 } 303 304 int 305 lcdpanelpoll(dev_t dev, int events, struct lwp *l) 306 { 307 int revents = 0; 308 309 if ((events & (POLLIN | POLLRDNORM)) != 0) { 310 struct lcdpanel_softc *sc; 311 312 sc = device_lookup_private(&lcdpanel_cd, minor(dev)); 313 if (lcdkp_scankey(&sc->sc_kp) != 0) { 314 revents = events & (POLLIN | POLLRDNORM); 315 } else { 316 selrecord(l, &sc->sc_selq); 317 callout_reset(&sc->sc_callout, LCDPANEL_POLLRATE, 318 lcdpanel_soft, sc); 319 } 320 } 321 322 return revents; 323 } 324 325 static void 326 lcdpanel_soft(void *arg) 327 { 328 struct lcdpanel_softc *sc = arg; 329 330 if (lcdkp_scankey(&sc->sc_kp) != 0) 331 selnotify(&sc->sc_selq, 0, 0); 332 else 333 callout_reset(&sc->sc_callout, LCDPANEL_POLLRATE, lcdpanel_soft, sc); 334 } 335