/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <types.h>30#include <lib.h>31#include <spinlock.h>32#include <platform/bus.h>33#include <lamebus/lser.h>34#include "autoconf.h"3536/* Registers (offsets within slot) */37#define LSER_REG_CHAR 0 /* Character in/out */38#define LSER_REG_WIRQ 4 /* Write interrupt status */39#define LSER_REG_RIRQ 8 /* Read interrupt status */4041/* Bits in the IRQ registers */42#define LSER_IRQ_ENABLE 143#define LSER_IRQ_ACTIVE 24445void46lser_irq(void *vsc)47{48struct lser_softc *sc = vsc;49uint32_t x;50bool clear_to_write = false;51bool got_a_read = false;52uint32_t ch = 0;5354spinlock_acquire(&sc->ls_lock);5556x = bus_read_register(sc->ls_busdata, sc->ls_buspos, LSER_REG_WIRQ);57if (x & LSER_IRQ_ACTIVE) {58x = LSER_IRQ_ENABLE;59sc->ls_wbusy = 0;60clear_to_write = true;61bus_write_register(sc->ls_busdata, sc->ls_buspos,62LSER_REG_WIRQ, x);63}6465x = bus_read_register(sc->ls_busdata, sc->ls_buspos, LSER_REG_RIRQ);66if (x & LSER_IRQ_ACTIVE) {67x = LSER_IRQ_ENABLE;68ch = bus_read_register(sc->ls_busdata, sc->ls_buspos,69LSER_REG_CHAR);70got_a_read = true;71bus_write_register(sc->ls_busdata, sc->ls_buspos,72LSER_REG_RIRQ, x);73}7475spinlock_release(&sc->ls_lock);7677if (clear_to_write && sc->ls_start != NULL) {78sc->ls_start(sc->ls_devdata);79}80if (got_a_read && sc->ls_input != NULL) {81sc->ls_input(sc->ls_devdata, ch);82}83}8485void86lser_write(void *vls, int ch)87{88struct lser_softc *ls = vls;8990spinlock_acquire(&ls->ls_lock);9192if (ls->ls_wbusy) {93/*94* We're not clear to write.95*96* This should not happen. It's the job of the driver97* attached to us to not write until we call98* ls->ls_start.99*100* (Note: if we're the console, the panic will go to101* lser_writepolled for printing, because we hold a102* spinlock and interrupts are off; it won't recurse.)103*/104panic("lser: Not clear to write\n");105}106ls->ls_wbusy = true;107108bus_write_register(ls->ls_busdata, ls->ls_buspos, LSER_REG_CHAR, ch);109110spinlock_release(&ls->ls_lock);111}112113static114void115lser_poll_until_write(struct lser_softc *sc)116{117uint32_t val;118119KASSERT(spinlock_do_i_hold(&sc->ls_lock));120121do {122val = bus_read_register(sc->ls_busdata, sc->ls_buspos,123LSER_REG_WIRQ);124}125while ((val & LSER_IRQ_ACTIVE) == 0);126}127128void129lser_writepolled(void *vsc, int ch)130{131struct lser_softc *sc = vsc;132bool irqpending = false;133134spinlock_acquire(&sc->ls_lock);135136if (sc->ls_wbusy) {137irqpending = true;138lser_poll_until_write(sc);139/* Clear the ready condition */140bus_write_register(sc->ls_busdata, sc->ls_buspos,141LSER_REG_WIRQ, LSER_IRQ_ENABLE);142}143144/* Send the character. */145bus_write_register(sc->ls_busdata, sc->ls_buspos, LSER_REG_CHAR, ch);146147/* Wait until it's done. */148lser_poll_until_write(sc);149150/*151* If there wasn't already an IRQ pending, clear the ready condition.152* But if there was, leave the ready condition, so we get to the153* interrupt handler in due course.154*/155if (!irqpending) {156bus_write_register(sc->ls_busdata, sc->ls_buspos,157LSER_REG_WIRQ, LSER_IRQ_ENABLE);158}159160spinlock_release(&sc->ls_lock);161}162163/*164* Mask the interrupt while we're writing in polling mode. Doing so165* causes the interrupt line to flap on every character, which makes166* the CPU receiving those interrupts upset.167*/168void169lser_startpolling(void *vsc)170{171struct lser_softc *sc = vsc;172sc->ls_maskinterrupt(sc->ls_busdata, sc->ls_buspos);173}174175void176lser_endpolling(void *vsc)177{178struct lser_softc *sc = vsc;179sc->ls_unmaskinterrupt(sc->ls_busdata, sc->ls_buspos);180}181182int183config_lser(struct lser_softc *sc, int lserno)184{185(void)lserno;186187/*188* Enable interrupting.189*/190191spinlock_init(&sc->ls_lock);192sc->ls_wbusy = false;193194bus_write_register(sc->ls_busdata, sc->ls_buspos,195LSER_REG_RIRQ, LSER_IRQ_ENABLE);196bus_write_register(sc->ls_busdata, sc->ls_buspos,197LSER_REG_WIRQ, LSER_IRQ_ENABLE);198199return 0;200}201202203