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 62struct 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 72struct lcd_message { 73 const char firstcol[LCDPANEL_VCOLS]; 74 const char secondcol[LCDPANEL_VCOLS]; 75}; 76static const struct lcd_message startup_message = { 77 "NetBSD/cobalt ", 78 "Starting up... " 79}; 80static const struct lcd_message halt_message = { 81 "NetBSD/cobalt ", 82 "Halting... " 83}; 84static const struct lcd_message reboot_message = { 85 "NetBSD/cobalt ", 86 "Rebooting... " 87}; 88 89static int lcdpanel_match(device_t, cfdata_t, void *); 90static void lcdpanel_attach(device_t, device_t, void *); 91static bool lcdpanel_shutdown(device_t, int); 92 93static void lcdpanel_soft(void *); 94 95static uint8_t lcdpanel_cbt_kprread(bus_space_tag_t, bus_space_handle_t); 96static uint8_t lcdpanel_cbt_hdreadreg(struct hd44780_chip *, uint32_t, uint32_t); 97static void lcdpanel_cbt_hdwritereg(struct hd44780_chip *, uint32_t, uint32_t, 98 uint8_t); 99 100dev_type_open(lcdpanelopen); 101dev_type_close(lcdpanelclose); 102dev_type_read(lcdpanelread); 103dev_type_write(lcdpanelwrite); 104dev_type_ioctl(lcdpanelioctl); 105dev_type_poll(lcdpanelpoll); 106 107const 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 122CFATTACH_DECL_NEW(lcdpanel, sizeof(struct lcdpanel_softc), 123 lcdpanel_match, lcdpanel_attach, NULL, NULL); 124 125static int 126lcdpanel_match(device_t parent, cfdata_t cf, void *aux) 127{ 128 129 return 1; 130} 131 132static void 133lcdpanel_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 191static bool 192lcdpanel_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 210static uint8_t 211lcdpanel_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 219static void 220lcdpanel_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 231static uint8_t 232lcdpanel_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 244int 245lcdpanelopen(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 252int 253lcdpanelclose(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 261int 262lcdpanelread(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 277int 278lcdpanelwrite(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 296int 297lcdpanelioctl(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 304int 305lcdpanelpoll(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 325static void 326lcdpanel_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