files.vx115 # # Configuration info for Agere Vx115 CPU support # file arch/arm/arm32/irq_dispatch.S # standard memory-mapped bus ops file arch/arm/vx115/vx115_io.c # vx115 onchip buses: have ahb, apb # vx115 ahb device vx115_ahb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic attach vx115_ahb at mainbus file arch/arm/vx115/vx115_ahb.c # vx115 apb device vx115_apb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic attach vx115_apb at mainbus file arch/arm/vx115/vx115_apb.c # clock device device vx115_clk attach vx115_clk at vx115_apb file arch/arm/vx115/vx115_clk.c # interrupt controller device vx115_pic attach vx115_pic at vx115_apb file arch/arm/vx115/vx115_intr.c # vx115 serial device device vx115_com: tty attach vx115_com at vx115_apb file arch/arm/vx115/vx115_com.c

vx115_apb.c /* * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu> * All rights reserved. * * Based on ixp12x0.c * * Copyright (c) 2002, 2003 * Ichiro FUKUHARA <ichiro@ichiro.org>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Ichiro FUKUHARA. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: ixp12x0.c,v 1.13 2004/08/30 15:05:16 drochner Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> #include <uvm/uvm.h> #include <machine/bus.h> #include <arm/vx115/vx115_reg.h> #include <arm/vx115/vx115_var.h> #include <locators.h> #define DEBUG_APB #ifdef DEBUG_APB #define DPRINTF(fmt...) printf(fmt) #else #define DPRINTF(fmt...) #endif static int vx115_apb_match(struct device *parent, struct cfdata *cf, void *aux); static void vx115_apb_attach(struct device *parent, struct device *self, void *aux); static int vx115_apb_search(struct device * parent, struct cfdata * cf, void *aux); CFATTACH_DECL(vx115_apb, sizeof(struct vx115_softc), vx115_apb_match, vx115_apb_attach, NULL, NULL); static int vx115_apb_print(void *aux, const char *name) { struct vx115_attach_args *sa = (struct vx115_attach_args *) aux; if (sa->sa_size) aprint_normal(" addr 0x%lx", sa->sa_addr); if (sa->sa_size > 1) aprint_normal("-0x%lx", sa->sa_addr + sa->sa_size - 1); if (sa->sa_intr != VX115_APBCF_INTR_DEFAULT) aprint_normal(" intr %d", sa->sa_intr); if (sa->sa_index != VX115_APBCF_INDEX_DEFAULT) aprint_normal(" unit %d", sa->sa_index); aprint_normal("

"); return (UNCONF); } static int vx115_apb_match(struct device *parent, struct cfdata *cf, void *aux) { DPRINTF("vx115_apb_match

"); return (1); } void vx115_apb_attach(struct device *parent, struct device *self, void *aux) { struct vx115_softc *sc = (struct vx115_softc *) self; DPRINTF("

vx115_apb_attach

"); /* assign bus tag: standard vx115 bus ops */ sc->sc_iot = &vx115_bs_tag; /* attach devices */ config_search(vx115_apb_search, self, NULL); return; } int vx115_apb_search(struct device * parent, struct cfdata * cf, void *aux) { struct vx115_softc *sc = (struct vx115_softc *) parent; struct vx115_attach_args aa; DPRINTF("vx115_apb_search

"); aa.sa_iot = sc->sc_iot; aa.sa_addr = cf->cf_loc[VX115_APBCF_ADDR]; aa.sa_size = cf->cf_loc[VX115_APBCF_SIZE]; aa.sa_index = cf->cf_loc[VX115_APBCF_INDEX]; aa.sa_intr = cf->cf_loc[VX115_APBCF_INTR]; if (config_match(parent, cf, &aa)) config_attach(parent, cf, &aa, vx115_apb_print); return 0; }

vx115_clk.c /*$NetBSD: epclk.c,v 1.4 2005/02/26 12:00:52 simonb Exp $*/ /* * Agere Vx115 clock functions * Copyright (c) 2006, Jon Sevy <jsevy@cs.drexel.edu> * * Based on epclk.c * Copyright (c) 2004 Jesse Off * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: *This product includes software developed by the NetBSD *Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Driver for the Vx115 clock tick. * We use Timer 1 for the system clock */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: vx115_clk.c,v 1.4 2005/02/26 12:00:52 simonb Exp $"); #include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> #include <sys/time.h> #include <sys/device.h> #include <dev/clock_subr.h> #include <machine/bus.h> #include <machine/intr.h> #include <arm/cpufunc.h> #include <arm/vx115/vx115_reg.h> #include <arm/vx115/vx115_var.h> #include <opt_hz.h> /* for HZ */ #define DEBUG_CLK #ifdef DEBUG_CLK #define DPRINTF(fmt...) printf(fmt) #else #define DPRINTF(fmt...) #endif static int vx115_clk_match(struct device *, struct cfdata *, void *); static void vx115_clk_attach(struct device *, struct device *, void *); void rtcinit(void); /* callback functions for intr_functions */ static int vx115_clk_intr(void* arg); struct vx115_clk_softc { struct device device; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int sc_intr; }; static struct vx115_clk_softc *vx115_clk_sc = NULL; static struct timeval lasttv; /* Match value for clock timer; running at 16kHz, want HZ ticks per second */ /* BTW, we use HZ == 64 or HZ == 128 so have a nice divisor */ /* NOTE: don't change there without visiting the functions below which */ /* convert between timer counts and microseconds */ #define VX115_CLK_SOURCE (32768/2) #define VX115_TIMER_LATCH (VX115_CLK_SOURCE / HZ) #define VX115_USEC_PER_TICK (1000000 / HZ) static uint32_t vx115_timer_count_to_usec(uint32_t count) { uint32_t result; /* convert specified number of ticks to usec, and round up */ /* note that with 16 kHz tick rate, maximum count will be */ /* 256 (for HZ = 64), so we won't have overflow issues */ result = (1000000 * count) / (VX115_CLK_SOURCE); if ((result*VX115_CLK_SOURCE) != (count*1000000)) { /* round up */ result += 1; } return result; } /* This may only be called when overflow is avoided; typically, */ /* it will be used when usec < VX115_USEC_PER_TICK */ static uint32_t vx115_usec_to_timer_count(uint32_t usec) { uint32_t result; /* convert specified number of usec to timer ticks, and round up */ result = (VX115_CLK_SOURCE * usec) / 1000000; if ((result*1000000) != (usec*VX115_CLK_SOURCE)) { /* round up */ result += 1; } return result; } /* macros to simplify writing to the timer controller */ #define vx115_read_clk(offset) bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset) #define vx115_write_clk(offset, value) bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value) CFATTACH_DECL(vx115_clk, sizeof(struct vx115_clk_softc), vx115_clk_match, vx115_clk_attach, NULL, NULL); static int vx115_clk_match(struct device *parent, struct cfdata *match, void *aux) { DPRINTF("vx115_clk_match

"); return 2; } static void vx115_clk_attach(struct device *parent, struct device *self, void *aux) { struct vx115_clk_softc *sc = (struct vx115_clk_softc*) self; struct vx115_attach_args *sa = (struct vx115_attach_args*) aux; DPRINTF("vx115_clk_attach

"); sc->sc_iot = sa->sa_iot; sc->sc_intr = sa->sa_intr; if (vx115_clk_sc == NULL) vx115_clk_sc = sc; /* map bus space and get handle */ if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh) != 0) panic("%s: Cannot map registers", self->dv_xname); } /* * vx115_clk_intr: * *Handle the hardclock interrupt. */ static int vx115_clk_intr(void *arg) { struct vx115_clk_softc *sc = vx115_clk_sc; /* make sure it's the kernel timer that generated the interrupt */ /* need to do this since the interrupt line is shared by the */ /* other interval and PWM timers */ if (vx115_read_clk(TIM_TMRSR) & TIM_TMRSR_I1S) { /* clear the interrupt bit in the timer */ vx115_write_clk(TIM_TMRSR, TIM_TMRSR_I1S); /* call the kernel timer handler */ hardclock((struct clockframe*) arg); #if 0 if (hardclock_ticks % HZ == 0) printf("time %i sec

", hardclock_ticks/HZ); #endif return 1; } else { /* it's one of the other timers; just pass it on */ return 0; } } /* * setstatclockrate: * *Set the rate of the statistics clock. * *We assume that hz is either stathz or profhz, and that neither *will change after being set by cpu_initclocks(). We could *recalculate the intervals here, but that would be a pain. */ void setstatclockrate(int hz) { /* use hardclock */ } /* * cpu_initclocks: * *Initialize the clock and get it going. */ void cpu_initclocks(void) { struct vx115_clk_softc *sc = vx115_clk_sc; stathz = profhz = 0; /* set up and enable interval timer 1 as kernel timer, */ /* using 32kHz clock source */ /* Note that since clock rate divisor is 2 for TMRCNTRATE = 0 */ /* the actual clock rate is 16kHz */ vx115_write_clk(TIM_TMRCR, (TIM_TMRCR_ITE1 | TIM_TMRCR_ITC1_32K)); /* register interrupt handler */ vx115_intr_establish(sc->sc_intr, IPL_CLOCK, vx115_clk_intr, NULL); vx115_configure_irq(sc->sc_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH); vx115_enable_irq(sc->sc_intr); /* Enable interrupts from timer 1 */ vx115_write_clk(TIM_TMRIE, TIM_TMRIE_I1E); /* load the max register; when this happens, we're running */ vx115_write_clk(TIM_ITMAXC1, VX115_TIMER_LATCH); } /* * microtime: * *Fill in the specified timeval struct with the current time *accurate to the microsecond. */ void microtime(register struct timeval *tvp) { struct vx115_clk_softc *sc = vx115_clk_sc; u_int oldirqstate; u_int current_count; #ifdef DEBUG if (vx115_clk_sc == NULL) { printf("microtime: called before initialize vx115_clk

"); tvp->tv_sec = 0; tvp->tv_usec = 0; return; } #endif oldirqstate = disable_interrupts(I32_bit); /* get current timer count */ current_count = vx115_read_clk(TIM_ITCNT1); /* Fill in the timeval struct. */ *tvp = time; /* Refine the usec field using current timer count */ tvp->tv_usec += vx115_timer_count_to_usec(VX115_TIMER_LATCH - current_count); /* Make sure microseconds doesn't overflow. */ while (__predict_false(tvp->tv_usec >= 1000000)) { tvp->tv_usec -= 1000000; tvp->tv_sec++; } /* Make sure the time has advanced. */ if (__predict_false(tvp->tv_sec == lasttv.tv_sec && tvp->tv_usec <= lasttv.tv_usec)) { tvp->tv_usec = lasttv.tv_usec + 1; if (tvp->tv_usec >= 1000000) { tvp->tv_usec -= 1000000; tvp->tv_sec++; } } lasttv = *tvp; restore_interrupts(oldirqstate); } extern int hardclock_ticks; static void tdelay(unsigned int ticks) { u_int32_t start, end, current; current = hardclock_ticks; start = current; end = start + ticks; /* just loop for the specified number of ticks */ while (current < end) current = hardclock_ticks; } static void udelay(unsigned int usec) { struct vx115_clk_softc *sc = vx115_clk_sc; u_int32_t start, end, current; current = vx115_read_clk(TIM_ITCNT1); start = current; end = start - vx115_usec_to_timer_count(usec); if (end <= 0) { /* need for counter to wrap; adjust end value, and */ /* wait for current to be below end but above start */ end += VX115_TIMER_LATCH; while (!((current <= end) && (current > start))) current = vx115_read_clk(TIM_ITCNT1); } else { /* just wait until count value is at or below end value */ while (current > end) current = vx115_read_clk(TIM_ITCNT1); } } /* * delay: * *Delay for at least N microseconds. Note that due to our coarse clock, * our resolution is 61 us. But we round up so we'll wait at least as * long as requested. */ void delay(unsigned int usec) { #ifdef DEBUG if (vx115_clk_sc == NULL) { printf("delay: called before start vx115_clk

"); return; } #endif if (usec >= VX115_USEC_PER_TICK) { printf("delay: called with large value (requested delay = %i us)

", usec); /* have more than 1 tick; just do in ticks */ unsigned int ticks = usec/VX115_USEC_PER_TICK; if (ticks*VX115_USEC_PER_TICK != usec) ticks += 1; tdelay(ticks); } else { /* less than 1 tick; can do as usec */ udelay(usec); } } todr_chip_handle_t todr_handle; /* * todr_attach: * *Set the specified time-of-day register as the system real-time clock. */ void todr_attach(todr_chip_handle_t todr) { if (todr_handle) panic("todr_attach: rtc already configured"); todr_handle = todr; } /* * inittodr: * *Initialize time from the time-of-day register. */ #define MINYEAR 2003 /* minimum plausible year */ void inittodr(time_t base) { time_t deltat; int badbase; if (base < (MINYEAR - 1970) * SECYR) { printf("WARNING: preposterous time in file system

"); /* read the system clock anyway */ base = (MINYEAR - 1970) * SECYR; badbase = 1; } else badbase = 0; if (todr_handle == NULL || todr_gettime(todr_handle, (struct timeval *)&time) != 0 || time.tv_sec == 0) { /* * Believe the time in the file system for lack of * anything better, resetting the TODR. */ time.tv_sec = base; time.tv_usec = 0; if (todr_handle != NULL && !badbase) { printf("WARNING: preposterous clock chip time

"); resettodr(); } goto bad; } if (!badbase) { /* * See if we gained/lost two or more days; if * so, assume something is amiss. */ deltat = time.tv_sec - base; if (deltat < 0) deltat = -deltat; if (deltat < 2 * SECDAY) return;/* all is well */ printf("WARNING: clock %s %ld days

", time.tv_sec < base ? "lost" : "gained", (long)deltat / SECDAY); } bad: printf("WARNING: CHECK AND RESET THE DATE!

"); } /* * resettodr: * *Reset the time-of-day register with the current time. */ void resettodr(void) { if (time.tv_sec == 0) return; if ((todr_handle != NULL) && (todr_settime(todr_handle, (struct timeval *)&time) != 0)) printf("resettodr: failed to set time

"); }

vx115_com.c /* * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu> * All rights reserved. * * Based on ixp12x0_com.c * * Copyright (c) 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA and Naoto Shimazaki. * * This code is derived from software contributed to The NetBSD Foundation * by IWAMOTO Toshihiro. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)com.c 7.5 (Berkeley) 5/16/91 */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: vx115_com.c,v 1.19 2003/08/07 16:26:53 agc Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" #include "rnd.h" #if NRND > 0 && defined(RND_COM) #include <sys/rnd.h> #endif #include <sys/param.h> #include <sys/systm.h> #include <sys/types.h> #include <sys/conf.h> #include <sys/file.h> #include <sys/device.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/tty.h> #include <sys/uio.h> #include <sys/vnode.h> #include <machine/intr.h> #include <machine/bus.h> #include <arm/vx115/vx115_reg.h> #include <arm/vx115/vx115_com.h> #include <arm/vx115/vx115_com_var.h> #include <arm/vx115/vx115_var.h> #include <dev/cons.h> #define DEBUG_COM //#define LOWLEVEL_DEBUG_COM #ifdef DEBUG_COM #define DPRINTF(fmt...) printf(fmt) void COM_DEBUG_PRINT_STRING(char *str); #ifdef LOWLEVEL_DEBUG_COM #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) COM_DEBUG_PRINT_STRING(str) #else #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) #endif #else #define DPRINTF(fmt...) #define COM_DEBUG_PRINT_STRING(str) #define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) #endif /* Vx115 com softc struct */ struct vx115_com_softc { struct device sc_dev; bus_addr_t sc_addr; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; void *sc_softintr; struct tty *sc_tty; u_char *sc_rbuf, *sc_ebuf; u_char *sc_tba; u_int sc_tbc, sc_heldtbc; u_char *sc_rbget,*sc_rbput; u_int sc_rbavail; /* status flags */ u_int sc_hwflags, sc_swflags; u_int sc_rx_flags; int sc_tx_busy; int sc_tx_stopped; int sc_rx_ready; int sc_heldchange; /* control registers */ u_int sc_baud_divisor; u_int sc_rx_control; u_int sc_tx_control; u_int sc_char_counter_control; /* power management hooks */ int (*enable)(struct vx115_com_softc *); int (*disable)(struct vx115_com_softc *); int enabled; }; /* Vx115 com console softc struct */ struct vx115_com_cons_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_addr_t sc_addr; int sc_ospeed; tcflag_t sc_cflag; int sc_attached; }; /* Utility functions */ static void vx115_com_break(struct vx115_com_softc *sc, int onoff); static void vx115_com_shutdown(struct vx115_com_softc *sc); static void vx115_com_iflush(struct vx115_com_softc *); static void vx115_com_set_cr(struct vx115_com_softc *); static u_int cflag_to_rx_control(tcflag_t cflag); static u_int cflag_to_tx_control(tcflag_t cflag); /* tty functions */ static int vx115_com_param(struct tty *, struct termios *); static int vx115_com_hwiflow(struct tty *, int); static void vx115_com_start(struct tty *); /* Console functions */ static int vx115_com_cngetc(dev_t); static void vx115_com_cnputc(dev_t, int); static void vx115_com_cnpollc(dev_t, int); /* Soft interrupt functions */ static void vx115_com_soft(void* arg); inline static void vx115_com_tx_soft(struct vx115_com_softc *, struct tty *); inline static void vx115_com_rx_soft(struct vx115_com_softc *, struct tty *); /* Hardware interrupt functions */ static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh); static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh); static struct vx115_com_cons_softc vx115_com_cn_sc; static struct cnm_state vx115_com_cnm_state; extern struct cfdriver vx115_com_cd; dev_type_open(vx115_com_open); dev_type_close(vx115_com_close); dev_type_read(vx115_com_read); dev_type_write(vx115_com_write); dev_type_ioctl(vx115_com_ioctl); dev_type_stop(vx115_com_stop); dev_type_tty(vx115_com_tty); dev_type_poll(vx115_com_poll); /* char dev switch structure for tty */ const struct cdevsw vx115_com_cdevsw = { vx115_com_open, vx115_com_close, vx115_com_read, vx115_com_write, vx115_com_ioctl, vx115_com_stop, vx115_com_tty, vx115_com_poll, nommap, /* mmap not supported */ ttykqfilter, D_TTY }; /* console method struct */ struct consdev vx115_com_cons = { NULL, /* cn_probe: probe hardware and fill in consdev info */ NULL, /* cn_init: turn on as console */ vx115_com_cngetc, /* cn_getc: kernel getchar interface */ vx115_com_cnputc, /* cn_putc: kernel putchar interface */ vx115_com_cnpollc, /* cn_pollc: turn on and off polling */ NULL, /* cn_bell: ring bell */ NULL, /* cn_halt: stop device */ NULL, /* cn_flush: flush output */ NODEV, /* major/minor of device */ CN_NORMAL /* priority */ }; #ifndef DEFAULT_COMSPEED #define DEFAULT_COMSPEED 115200 #endif #define COMUNIT_MASK 0x7ffff #define COMDIALOUT_MASK 0x80000 #define COMUNIT(x) (minor(x) & COMUNIT_MASK) #define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK) #define COM_ISALIVE(sc) ((sc)->enabled != 0 && ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE)) #define COM_BARRIER(t,h,f) bus_space_barrier((t), (h), 0, COM_NPORTS, (f)) /* we're uniprocessor, so no need for locks */ #define COM_LOCK(sc) #define COM_UNLOCK(sc) /* macros to make code more readable */ #define REG_READ(offset) bus_space_read_4(iot, ioh, offset) #define REG_WRITE(offset,value) bus_space_write_4(iot, ioh, offset, value) #define SET(t,f) (t) |= (f) #define CLR(t,f) (t) &= ~(f) #define ISSET(t,f) ((t) & (f)) /* receive interrupts: FIFO threshold */ #define RECEIVE_INTERRUPT_MASK ASCC_RX_CTRL_FICE_TH /* character counter interrupt mask */ #define CHAR_CNT_CNTL_INTERRUPT_MASK (ASCC_CHAR_COUNTER_CONTROL_ENABLE_FREERUNNING | ASCC_CHAR_COUNTER_CONTROL_INT_ENABLE) /* transmit interrupts: FIFO threshold */ #define TRANSMIT_INTERRUPT_MASK ASCC_TX_CTRL_FICE_TH #ifdef DEBUG_COM void COM_DEBUG_PRINT_STRING(char *str) { while(*str != '\0') { bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; /* spin while tx fifo full */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ; /* write the next char */ REG_WRITE(ASCC_TX_RX_FIFO, *str++); } } #endif static int vx115_com_match(struct device *, struct cfdata *, void *); static void vx115_com_attach(struct device *, struct device *, void *); static void vx115_com_attach_subr(struct vx115_com_softc *sc); static int vx115_com_intr(void* arg); CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc), vx115_com_match, vx115_com_attach, NULL, NULL); /********************************************************************* * Autoconfig functions */ static int vx115_com_match(struct device *parent, struct cfdata *match, void *aux) { COM_DEBUG_PRINT_STRING("vx115_com_match

"); if (strcmp(match->cf_name, "vx115_com") == 0) return 1; return 0; } static void vx115_com_attach(struct device *parent, struct device *self, void *aux) { struct vx115_com_softc *sc = (struct vx115_com_softc *)self; struct vx115_attach_args *sa = (struct vx115_attach_args *)aux; sc->sc_iot = sa->sa_iot; sc->sc_addr = sa->sa_addr; printf("

"); COM_DEBUG_PRINT_STRING("vx115_com_attach

"); bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh); vx115_com_attach_subr(sc); vx115_intr_establish(sa->sa_intr, IPL_SERIAL, vx115_com_intr, sc); vx115_configure_irq(sa->sa_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH); vx115_enable_irq(sa->sa_intr); } static void vx115_com_attach_subr(struct vx115_com_softc *sc) { struct tty *tp; COM_DEBUG_PRINT_STRING("vx115_com_attach_subr

"); /* check if this is com port for console */ if ((sc->sc_iot == vx115_com_cn_sc.sc_iot) && (sc->sc_addr == vx115_com_cn_sc.sc_addr)) { vx115_com_cn_sc.sc_attached = 1; //delay(10000); /* wait for output to finish */ /* make sure the console is always "hardwired" */ SET(sc->sc_hwflags, COM_HW_CONSOLE); SET(sc->sc_swflags, TIOCFLAG_SOFTCAR); } tp = ttymalloc(); tp->t_oproc = vx115_com_start; tp->t_param = vx115_com_param; tp->t_hwiflow = vx115_com_hwiflow; sc->sc_tty = tp; sc->sc_rbuf = malloc(VX115_COM_RING_SIZE << 1, M_DEVBUF, M_NOWAIT); sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; sc->sc_rbavail = VX115_COM_RING_SIZE; if (sc->sc_rbuf == NULL) { printf("%s: unable to allocate ring buffer

", sc->sc_dev.dv_xname); return; } sc->sc_ebuf = sc->sc_rbuf + (VX115_COM_RING_SIZE << 1); sc->sc_tbc = 0; /* initialize control registers: 8-B-1 and appropriate baud rate */ /* set FIFO thresholds to the halfway point */ sc->sc_baud_divisor = VX115_BAUD_DIVISOR(DEFAULT_COMSPEED); sc->sc_rx_control = ASCC_RX_CTRL_RXCHSIZE_8 | ASCC_RX_CTRL_RXP_NO | ASCC_RX_CTRL_RXTH32 | ASCC_RX_CTRL_BRDEN; sc->sc_tx_control = ASCC_TX_CTRL_TXCHSIZE_8 | ASCC_TX_CTRL_TXP_NO | ASCC_TX_CTRL_TXTH32; /* clear disable bit, if set */ sc->sc_rx_control &= ~ASCC_RX_CTRL_RXID; sc->sc_tx_control &= ~ASCC_TX_CTRL_TXID; /* also set the rx timeout count */ sc->sc_char_counter_control = (0x100 & ASCC_CHAR_COUNTER_CONTROL_COUNT_MASK); tty_attach(tp); if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { int maj; /* locate the major number */ maj = cdevsw_lookup_major(&vx115_com_cdevsw); cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); aprint_normal("%s: major = %i: console

", sc->sc_dev.dv_xname, maj); } sc->sc_softintr = softintr_establish(IPL_SOFTSERIAL, vx115_com_soft, sc); #if NRND > 0 && defined(RND_COM) rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname, RND_TYPE_TTY, 0); #endif /* if there are no enable/disable functions, assume the device is always enabled */ if (!sc->enable) sc->enabled = 1; SET(sc->sc_hwflags, COM_HW_DEV_OK); } /************************************************************** * cdevsw functions */ int vx115_com_open(dev_t dev, int flag, int mode, struct proc *p) { struct vx115_com_softc *sc; struct tty *tp; int s, s2; int error; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_open

"); sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) || sc->sc_rbuf == NULL) return (ENXIO); if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) return (ENXIO); #ifdef KGDB /* * If this is the kgdb port, no other use is permitted. */ if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) return (EBUSY); #endif tp = sc->sc_tty; if (ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); s = spltty(); /* * Do the following iff this is a first open. */ if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { struct termios t; tp->t_dev = dev; s2 = splserial(); COM_LOCK(sc); if (sc->enable) { if ((*sc->enable)(sc)) { COM_UNLOCK(sc); splx(s2); splx(s); printf("%s: device enable failed

", sc->sc_dev.dv_xname); return (EIO); } sc->enabled = 1; } /* Turn on receive interrupts. */ sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); COM_UNLOCK(sc); splx(s2); /* * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ t.c_ispeed = 0; if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { t.c_ospeed = vx115_com_cn_sc.sc_ospeed; t.c_cflag = vx115_com_cn_sc.sc_cflag; } else { t.c_ospeed = TTYDEF_SPEED; t.c_cflag = TTYDEF_CFLAG; } if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) SET(t.c_cflag, CLOCAL); if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) SET(t.c_cflag, CRTSCTS); if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) SET(t.c_cflag, MDMBUF); /* Make sure vx115_com_param() will do something. */ tp->t_ospeed = 0; (void) vx115_com_param(tp, &t); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; ttychars(tp); ttsetwater(tp); s2 = splserial(); COM_LOCK(sc); /* Clear the input ring, and unblock. */ sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; sc->sc_rbavail = VX115_COM_RING_SIZE; vx115_com_iflush(sc); CLR(sc->sc_rx_flags, RX_ANY_BLOCK); #ifdef COM_DEBUG if (vx115_com_debug) comstatus(sc, "vx115_com_open "); #endif COM_UNLOCK(sc); splx(s2); } splx(s); error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); if (error) goto bad; error = (*tp->t_linesw->l_open)(dev, tp); if (error) goto bad; return (0); bad: if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { /* * We failed to open the device, and nobody else had it opened. * Clean up the state as appropriate. */ vx115_com_shutdown(sc); } return (error); } int vx115_com_close(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_close

"); /* XXX This is for cons.c. */ if (!ISSET(tp->t_state, TS_ISOPEN)) return (0); (*tp->t_linesw->l_close)(tp, flag); ttyclose(tp); if (COM_ISALIVE(sc) == 0) return (0); if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { /* * Although we got a last close, the device may still be in * use; e.g. if this was the dialout node, and there are still * processes waiting for carrier on the non-dialout node. */ vx115_com_shutdown(sc); } return (0); } int vx115_com_read(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_read

"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_read)(tp, uio, flag)); } int vx115_com_write(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_write

"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_write)(tp, uio, flag)); } int vx115_com_poll(dev, events, p) dev_t dev; int events; struct proc *p; { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_poll

"); if (COM_ISALIVE(sc) == 0) return (EIO); return ((*tp->t_linesw->l_poll)(tp, events, p)); } /* Stop output on a line. */ void vx115_com_stop(struct tty *tp, int flag) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_stop

"); s = splserial(); COM_LOCK(sc); if (ISSET(tp->t_state, TS_BUSY)) { /* Stop transmitting at the next chunk. */ sc->sc_tbc = 0; sc->sc_heldtbc = 0; if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } COM_UNLOCK(sc); splx(s); } struct tty * vx115_com_tty(dev_t dev) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tty

"); return (tp); } int vx115_com_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev)); struct tty *tp = sc->sc_tty; int error; int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_ioctl

"); if (COM_ISALIVE(sc) == 0) return (EIO); error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); error = ttioctl(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); error = 0; s = splserial(); COM_LOCK(sc); switch (cmd) { case TIOCSBRK: vx115_com_break(sc, 1); break; case TIOCCBRK: vx115_com_break(sc, 0); break; case TIOCGFLAGS: *(int *)data = sc->sc_swflags; break; case TIOCSFLAGS: error = suser(p->p_ucred, &p->p_acflag); if (error) break; sc->sc_swflags = *(int *)data; break; default: error = EPASSTHROUGH; break; } COM_UNLOCK(sc); splx(s); return (error); } /************************************************************** * Utility functions */ /* Flush the hardware receive FIFO */ static void vx115_com_iflush(struct vx115_com_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int timo; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_iflush

"); timo = 50000; /* flush any pending I/O */ while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) && --timo ) { REG_READ(ASCC_TX_RX_FIFO); } #ifdef DIAGNOSTIC if (!timo) printf("%s: com_iflush timeout

", sc->sc_dev.dv_xname); #endif } /* Set or clear generation of break condition */ static void vx115_com_break(struct vx115_com_softc *sc, int onoff) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_break

"); if (onoff) sc->sc_tx_control |= ASCC_TX_CTRL_BREAK; else sc->sc_tx_control &= ~ASCC_TX_CTRL_BREAK; if (!sc->sc_heldchange) { if (sc->sc_tx_busy) { sc->sc_heldtbc = sc->sc_tbc; sc->sc_tbc = 0; sc->sc_heldchange = 1; } else { vx115_com_set_cr(sc); } } } static void vx115_com_shutdown(struct vx115_com_softc *sc) { int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_shutdown

"); s = splserial(); COM_LOCK(sc); /* Clear any break condition set with TIOCSBRK. */ vx115_com_break(sc, 0); /* Turn off interrupts. */ sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; /* set disable bit */ sc->sc_rx_control |= ASCC_RX_CTRL_RXID; sc->sc_tx_control |= ASCC_TX_CTRL_TXID; vx115_com_set_cr(sc); if (sc->disable) { #ifdef DIAGNOSTIC if (!sc->enabled) panic("vx115_com_shutdown: not enabled?"); #endif (*sc->disable)(sc); sc->enabled = 0; } COM_UNLOCK(sc); splx(s); } static u_int cflag_to_rx_control(tcflag_t cflag) { u_int cr = 0; COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_rx_control

"); if (cflag & PARENB) cr |= (cflag & PARODD) ? ASCC_RX_CTRL_RXP_ODD : ASCC_RX_CTRL_RXP_EVEN; cr |= ((cflag & CSIZE) == CS8) ? ASCC_RX_CTRL_RXCHSIZE_8 : ASCC_RX_CTRL_RXCHSIZE_7; cr |= (cflag & CSTOPB) ? ASCC_RX_CTRL_RXSTOP2 : ASCC_RX_CTRL_RXSTOP1; return (cr); } static u_int cflag_to_tx_control(tcflag_t cflag) { u_int cr = 0; COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_tx_control

"); if (cflag & PARENB) cr |= (cflag & PARODD) ? ASCC_TX_CTRL_TXP_ODD : ASCC_TX_CTRL_TXP_EVEN; cr |= ((cflag & CSIZE) == CS8) ? ASCC_TX_CTRL_TXCHSIZE_8 : ASCC_TX_CTRL_TXCHSIZE_7; cr |= (cflag & CSTOPB) ? ASCC_TX_CTRL_TXSTOP2 : ASCC_TX_CTRL_TXSTOP1; return (cr); } static void vx115_com_set_cr(struct vx115_com_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_set_cr

"); /* set control regs and baud divisor */ REG_WRITE(ASCC_RX_CONTROL, sc->sc_rx_control); REG_WRITE(ASCC_TX_CONTROL, sc->sc_tx_control); REG_WRITE(ASCC_BAUD_DIVISOR, sc->sc_baud_divisor); REG_WRITE(ASCC_CHAR_COUNTER_CONTROL, sc->sc_char_counter_control); } /************************************************************************** * tty functions * Assigned to associated tty struct fields in vx115_com_open */ /* Assigned to standard termios line discipline t_linesw->l_start method. */ /* Called from within our vx115_txsoft method. */ static void vx115_com_start(struct tty *tp) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start

"); if (COM_ISALIVE(sc) == 0) return; s = spltty(); if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) goto out; if (sc->sc_tx_stopped) goto out; if (tp->t_outq.c_cc <= tp->t_lowat) { if (ISSET(tp->t_state, TS_ASLEEP)) { CLR(tp->t_state, TS_ASLEEP); wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); if (tp->t_outq.c_cc == 0) goto out; } /* output the first contiguous region of buffer space. */ /* set spl to splserial to prevent race condition on sc_tbc,sc_tba, sc_tx_busy */ (void)splserial(); COM_LOCK(sc); sc->sc_tba = tp->t_outq.c_cf; sc->sc_tbc = ndqb(&tp->t_outq, 0); if (sc->sc_tbc > 0) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start: sc->sc_tbc > 0

"); SET(tp->t_state, TS_BUSY); sc->sc_tx_busy = 1; /* Enable transmit interrupts */ sc->sc_tx_control |= TRANSMIT_INTERRUPT_MASK; vx115_com_set_cr(sc); } COM_UNLOCK(sc); out: splx(s); return; } static int vx115_com_param(struct tty *tp, struct termios *t) { struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev)); int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_param

"); if (COM_ISALIVE(sc) == 0) return (EIO); if (t->c_ispeed && t->c_ispeed != t->c_ospeed) return (EINVAL); sc->sc_baud_divisor = VX115_BAUD_DIVISOR(t->c_ospeed); /* * For the console, always force CLOCAL and !HUPCL, so that the port * is always active. */ if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { SET(t->c_cflag, CLOCAL); CLR(t->c_cflag, HUPCL); } /* * If there were no changes, don't do anything. This avoids dropping * input and improves performance when all we did was frob things like * VMIN and VTIME. */ if ((tp->t_ospeed == t->c_ospeed) && (tp->t_cflag == t->c_cflag)) return (0); sc->sc_rx_control |= cflag_to_rx_control(t->c_cflag); sc->sc_tx_control |= cflag_to_tx_control(t->c_cflag); s = splserial(); COM_LOCK(sc); /* don't use hardware flow control */ /* copy to tty */ tp->t_ispeed = 0; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; if (!sc->sc_heldchange) { if (sc->sc_tx_busy) { sc->sc_heldtbc = sc->sc_tbc; sc->sc_tbc = 0; sc->sc_heldchange = 1; } else vx115_com_set_cr(sc); } COM_UNLOCK(sc); splx(s); /* * Update the tty layer's idea of the carrier bit. * We tell tty the carrier is always on. */ (void) (*tp->t_linesw->l_modem)(tp, 1); #ifdef COM_DEBUG if (com_debug) comstatus(sc, "comparam "); #endif if (!ISSET(t->c_cflag, CHWFLOW)) { if (sc->sc_tx_stopped) { sc->sc_tx_stopped = 0; vx115_com_start(tp); } } return (0); } static int vx115_com_hwiflow(tp, block) struct tty *tp; int block; { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_hwiflow

"); return (0); } /************************************************************** * Console functions */ int vx115_com_cnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh, int ospeed, tcflag_t cflag) { unsigned int baud_divisor, rx_control, tx_control; cn_tab = &vx115_com_cons; cn_init_magic(&vx115_com_cnm_state); //cn_set_magic("\047\001"); /* default magic is BREAK */ vx115_com_cn_sc.sc_iot = iot; vx115_com_cn_sc.sc_ioh = ioh; vx115_com_cn_sc.sc_addr = iobase; vx115_com_cn_sc.sc_ospeed = ospeed; vx115_com_cn_sc.sc_cflag = cflag; baud_divisor = VX115_BAUD_DIVISOR(ospeed); rx_control = cflag_to_rx_control(cflag); tx_control = cflag_to_tx_control(cflag); /* set rx and tx to interrupt on threshold achievement, */ /* and set thresholds to the halfway point */ rx_control |= ASCC_RX_CTRL_FICE_TH | ASCC_RX_CTRL_RXTH32; tx_control |= ASCC_TX_CTRL_FICE_TH | ASCC_TX_CTRL_TXTH32; /* clear disable bit, if set */ rx_control &= ~ASCC_RX_CTRL_RXID; tx_control &= ~ASCC_TX_CTRL_TXID; /* enable the UART; note that interrupts will not yet be hooked up */ /* however, the kernel printf function depends only on vx115_com_cnputc, */ /* which doesn't require that interrupts be ready, so we get output */ bus_space_write_4(iot, ioh, ASCC_BAUD_DIVISOR, baud_divisor); bus_space_write_4(iot, ioh, ASCC_RX_CONTROL, rx_control); bus_space_write_4(iot, ioh, ASCC_TX_CONTROL, tx_control); return (0); } static void vx115_com_cnpollc(dev, on) dev_t dev; int on; { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnpollc

"); } static void vx115_com_cnputc(dev_t dev, int c) { int s; bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnputc

"); s = splserial(); /* spin while tx fifo full */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ; /* write the char */ REG_WRITE(ASCC_TX_RX_FIFO, c); splx(s); } static int vx115_com_cngetc(dev_t dev) { int c; int s; bus_space_tag_t iot = vx115_com_cn_sc.sc_iot; bus_space_handle_t ioh = vx115_com_cn_sc.sc_ioh; //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cngetc

"); s = splserial(); /* spin while rx fifo empty */ while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) ; /* get the char */ c = REG_READ(ASCC_TX_RX_FIFO); c &= 0xff; splx(s); return (c); } /************************************************************** * Soft interrupt functions */ inline static void vx115_com_tx_soft(struct vx115_com_softc *sc, struct tty *tp) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_soft

"); CLR(tp->t_state, TS_BUSY); /* adjust tty buffer pointers to account for data that's been sent */ if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf)); /* for the standard tty line discipline, just calls vx115_com_start; */ /* this will check to see if any new data's available, and set transmit */ /* interrupt if so to begin transmission */ (*tp->t_linesw->l_start)(tp); } inline static void vx115_com_rx_soft(struct vx115_com_softc *sc, struct tty *tp) { int (*rint) __P((int c, struct tty *tp)) = tp->t_linesw->l_rint; u_char *get, *end; u_int cc, scc; u_char lsr; int code; int s; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_soft

"); end = sc->sc_ebuf; get = sc->sc_rbget; scc = cc = VX115_COM_RING_SIZE - sc->sc_rbavail; while (cc) { code = get[0]; lsr = get[1]; if (ISSET(lsr, (ASCC_TX_RX_FIFO_ERROR_RPE_RFE | ASCC_TX_RX_FIFO_ERROR_BRE))) { if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_RPE_RFE)) { /* we have either a parity or framing error; unfortunately, */ /* the ASCC doesn't tell us which, so just call it parity */ SET(code, TTY_PE); } if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_BRE)) { /* break condition is reported through TTY_FE */ SET(code, TTY_FE); } } if ((*rint)(code, tp) != 0) { /* error; not sure what to do... */ } get += 2; if (get >= end) get = sc->sc_rbuf; cc--; } if (cc != scc) { sc->sc_rbget = get; s = splserial(); COM_LOCK(sc); sc->sc_rbavail += scc - cc; if (sc->sc_rbavail >= 1) { /* buffers should be ok again, release possible block */ if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) { CLR(sc->sc_rx_flags, RX_IBUF_FULL); /* enable receive interrupts */ sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); } } COM_UNLOCK(sc); splx(s); } } static void vx115_com_soft(void* arg) { struct vx115_com_softc *sc = arg; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_soft

"); if (COM_ISALIVE(sc) == 0) return; if (sc->sc_rx_ready) { sc->sc_rx_ready = 0; vx115_com_rx_soft(sc, sc->sc_tty); } if (!sc->sc_tx_busy) { /* previous transmissions complete; see if have any new stuff to send */ vx115_com_tx_soft(sc, sc->sc_tty); } } /************************************************************** * Hardware interrupt functions */ static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh) { u_char *put, *end; u_int cc; u_int32_t c; COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_hard

"); if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) { /* overflow - turn off interrupts */ sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); return; } /* get receive buffer parameters */ end = sc->sc_ebuf; put = sc->sc_rbput; cc = sc->sc_rbavail; /* copy characters from ASCC FIFO to receive buffer */ /* while FIFO not empty and buffer not full; also */ /* check for magic character sequence */ while ((cc > 0) && !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE)) { c = REG_READ(ASCC_TX_RX_FIFO); put[0] = c & 0xff; put[1] = (c >> 8) & 0xff; cn_check_magic(sc->sc_tty->t_dev, put[0], vx115_com_cnm_state); put += 2; if (put >= end) put = sc->sc_rbuf; cc--; } /* * Current string of incoming characters ended because * no more data was available or we ran out of space. * Schedule a receive event if any data was received. * If we're out of space, turn off receive interrupts. */ sc->sc_rbput = put; sc->sc_rbavail = cc; sc->sc_rx_ready = 1; /* * If we're out of space, disable receive interrupts * until the queue has drained a bit. */ if (cc == 0) { SET(sc->sc_rx_flags, RX_IBUF_FULL); sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK; sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK; vx115_com_set_cr(sc); } } static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh) { COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_hard

"); /* If we've delayed a parameter change, do it now, and restart output. */ if (sc->sc_heldchange) { vx115_com_set_cr(sc); sc->sc_heldchange = 0; sc->sc_tbc = sc->sc_heldtbc; sc->sc_heldtbc = 0; } /* output the next chunk of the contiguous buffer, if any. */ if (sc->sc_tbc > 0) { int n = 0; while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) ) { if (n >= sc->sc_tbc) break; REG_WRITE(ASCC_TX_RX_FIFO, 0xff & *(sc->sc_tba + n)); n++; } sc->sc_tbc -= n; sc->sc_tba += n; } if (sc->sc_tbc == 0) { /* we've finished with transmit buffer; disable transmit completion interrupts; */ /* will be reenabled when vx115_com_start called to transmit more */ sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK; vx115_com_set_cr(sc); sc->sc_tx_busy = 0; } } static int vx115_com_intr(void* arg) { struct vx115_com_softc *sc = arg; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int status; if (COM_ISALIVE(sc) == 0) return (0); COM_LOCK(sc); /* read main status reg to see what's up */ status = REG_READ(ASCC_STATUS); COM_LOWLEVEL_DEBUG_PRINT_STRING("i

"); do { if (status & ASCC_STATUS_ROE) { /* receive overrun */ COM_LOWLEVEL_DEBUG_PRINT_STRING("o

"); } if (status & ASCC_STATUS_RPE) { /* parity error */ COM_LOWLEVEL_DEBUG_PRINT_STRING("p

"); } if (status & ASCC_STATUS_RFE) { /* framing error */ COM_LOWLEVEL_DEBUG_PRINT_STRING("f

"); } if (status & ASCC_STATUS_MISC_ANY) { /* it's a modem status interrupt */ COM_LOWLEVEL_DEBUG_PRINT_STRING("m

"); } if (status & (ASCC_STATUS_RFT | ASCC_STATUS_CCE0)) { /* it's a receive FIFO interrupt; either hit */ /* threshold, or timed out with chars avail */ COM_LOWLEVEL_DEBUG_PRINT_STRING("r

"); vx115_com_rx_hard(sc, iot, ioh); } if (status & ASCC_STATUS_TFT) { /* it's a transmit FIFO interrupt */ COM_LOWLEVEL_DEBUG_PRINT_STRING("t

"); vx115_com_tx_hard(sc, iot, ioh); } /* see if we're done */ status = REG_READ(ASCC_STATUS); } while (status); COM_UNLOCK(sc); /* Wake up the poller. */ softintr_schedule(sc->sc_softintr); return (1); }

vx115_intr.h /* * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu> * All rights reserved. * * Based on code by Jason R. Thorpe * * Copyright (c) 2002 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: *This product includes software developed for the NetBSD Project by *Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _VX115_INTR_H_ #define _VX115_INTR_H_ #define ARM_IRQ_HANDLER _C_LABEL(vx115_irq_dispatcher) #ifndef _LOCORE #include <arm/cpu.h> #include <arm/armreg.h> #include <arm/cpufunc.h> #include <arm/softintr.h> #include <machine/atomic.h> #include <machine/bus.h> #include <machine/intr.h> #include <arm/vx115/vx115_reg.h> typedef int (* vx115_irq_handler_t)(void *); #define NR_IRQS 32 #define VALID_IRQ(i) ((i!=24) && (i!=27) && (i<NR_IRQS)) #define IRQ_EXT2 1 #define IRQ_EXT3 2 #define IRQ_EXT4 3 #define IRQ_EXT5 4 #define IRQ_EXT6 5 #define IRQ_EXT7 6 #define IRQ_DMA1_ERROR 7 #define IRQ_DMA1 8 #define IRQ_TIMER1 9 #define IRQ_AGPIOA 10 #define IRQ_AGPIOB 11 #define IRQ_SDMCC0 12 #define IRQ_SDMCC1 13 #define IRQ_SSP3 14 #define IRQ_I2C 15 #define IRQ_SSP1 16 #define IRQ_GPIOA 17 #define IRQ_GPIOB 18 #define IRQ_UART0 19 #define IRQ_KEYPAD 20 #define IRQ_DISPLAY 21 #define IRQ_DISP_SYNC 22 #define IRQ_CAMERA 23 #define IRQ_RESERVED24 24 #define IRQ_DSP_PCU 25 #define IRQ_ARM7_PCU 26 #define IRQ_RESERVED27 27 #define IRQ_UART0_WAKE 28 #define IRQ_USB_OTG 29 #define IRQ_USB_EXTINT 30 #define IRQ_SWI 31 /* priorities assigned to each interrupt source */ #define IRQ_TIMER1_PRIORITY 1 #define IRQ_DMA1_ERROR_PRIORITY 2 #define IRQ_DMA1_PRIORITY 3 #define IRQ_DISPLAY_PRIORITY 4 #define IRQ_DISP_SYNC_PRIORITY 5 #define IRQ_SSP1_PRIORITY 6 #define IRQ_SSP3_PRIORITY 7 #define IRQ_UART0_WAKE_PRIORITY 8 #define IRQ_UART0_PRIORITY 9 #define IRQ_SDMCC0_PRIORITY 10 #define IRQ_SDMCC1_PRIORITY 11 #define IRQ_I2C_PRIORITY 12 #define IRQ_KEYPAD_PRIORITY 13 #define IRQ_GPIOA_PRIORITY 14 #define IRQ_GPIOB_PRIORITY 15 #define IRQ_USB_EXTINT_PRIORITY 16 #define IRQ_USB_OTG_PRIORITY 17 #define IRQ_DSP_PCU_PRIORITY 18 #define IRQ_ARM7_PCU_PRIORITY 19 #define IRQ_SWI_PRIORITY 20 #define IRQ_CAMERA_PRIORITY 21 #define IRQ_EXT2_PRIORITY 22 #define IRQ_EXT3_PRIORITY 23 #define IRQ_AGPIOA_PRIORITY 24 #define IRQ_AGPIOB_PRIORITY 25 #define IRQ_EXT4_PRIORITY 26 #define IRQ_EXT5_PRIORITY 27 #define IRQ_EXT6_PRIORITY 28 #define IRQ_EXT7_PRIORITY 29 #define IRQ_RESERVED24_PRIORITY 30 #define IRQ_RESERVED27_PRIORITY 31 extern __volatile int current_spl_level; extern __volatile int softint_pending; extern int vx115_pic_spl_soft_mask[]; void vx115_do_pending(void); /* * We use all of the bits in the hardware interrupt controller, * so we use a separate mask for the soft interrupts, and just * map them to corresponding bits */ #define SI_TO_IRQBIT(si) (1U<<(si)) #define VX115_IRQ_MIN 0 #define VX115_IRQ_MAX 32 struct vx115_pic_softc { struct device dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; }; extern struct vx115_pic_softc *vx115_pic_sc; extern int vx115_pic_spl_mask[NIPL]; extern int vx115_pic_spl_soft_mask[NIPL]; /* macros to simplify writing to the interrupt controller */ #define vx115_read_pic(offset) bus_space_read_4(vx115_pic_sc->sc_iot, vx115_pic_sc->sc_ioh, offset) #define vx115_write_pic(offset, value) bus_space_write_4(vx115_pic_sc->sc_iot, vx115_pic_sc->sc_ioh, offset, value) /* * Utility function for interrupt handler. */ static __inline int find_first_bit( uint32_t bits ) { int count; /* CLZ is available only on ARMv5 */ asm( "clz %0, %1" : "=r" (count) : "r" (bits) ); return 31-count; } static __inline void vx115_setipl(int new) { current_spl_level = new; /* enable hardware interrupts appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]); /* clear hardware interrupts not appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]); } static __inline void vx115_splx(int new) { int psw; psw = disable_interrupts(I32_bit); vx115_setipl(new); restore_interrupts(psw); /* if there are software interrupts pending, process */ if (softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) vx115_do_pending(); } static __inline int vx115_splraise(int ipl) { int old, psw; psw = disable_interrupts(I32_bit); old = current_spl_level; if( ipl > old ) vx115_setipl(ipl); restore_interrupts(psw); return (old); } static __inline int vx115_spllower(int ipl) { int old, psw; psw = disable_interrupts(I32_bit); old = current_spl_level; if( ipl < old ) vx115_setipl(ipl); restore_interrupts(psw); /* if there are software interrupts pending, process */ if (softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) vx115_do_pending(); return(old); } static __inline void vx115_setsoftintr(int si) { atomic_set_bit( (u_int *)&softint_pending, SI_TO_IRQBIT(si) ); /* Process unmasked pending soft interrupts. */ if ( softint_pending & vx115_pic_spl_soft_mask[current_spl_level] ) vx115_do_pending(); } int _splraise(int); int _spllower(int); void splx(int); void _setsoftintr(int); #if !defined(EVBARM_SPL_NOINLINE) #define splx(new) vx115_splx(new) #define _spllower(ipl) vx115_spllower(ipl) #define _splraise(ipl) vx115_splraise(ipl) #define _setsoftintr(si) vx115_setsoftintr(si) #endif /* !EVBARM_SPL_NOINTR */ /* * This function *MUST* be called very early on in a port's * initarm() function, before ANY spl*() functions are called. * * The parameters are the virtual address of the Vx115's Interrupt * Controller registers and the size of the reg region. */ void vx115_intr_bootstrap(bus_addr_t addr, bus_size_t size); void vx115_irq_dispatcher(void *); void *vx115_intr_establish(int irqno, int level, int (*func)(void *), void *cookie); void vx115_update_intr_masks(int irqno, int level); #define VX115_INT_DISABLED (unsigned int)0x00000000 #define VX115_INT_ENABLED (unsigned int)0x00000001 #define VX115_INT_SENSE_LEVEL (unsigned int)0x00000000 #define VX115_INT_SENSE_EDGE (unsigned int)0x00000002 #define VX115_INT_POLARITY_LOW (unsigned int)0x00000000 #define VX115_INT_POLARITY_HIGH (unsigned int)0x00000004 #define VX115_INT_POLARITY_HIGH_LOW (unsigned int)0x00000000 #define VX115_INT_POLARITY_LOW_HIGH (unsigned int)0x00000004 int vx115_configure_irq(unsigned int irq, unsigned int sense, unsigned int polarity); void vx115_enable_irq(unsigned int irq); void vx115_disable_irq(unsigned int irq); #endif /* ! _LOCORE */ #endif /* _VX115_INTR_H_ */

vx115_intr.c /* Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu> * * Based on PXA interrupt controller, * Copyright (c) 2002 Genetec Corporation. All rights reserved. * Written by Hiroyuki Bessho for Genetec Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: *This product includes software developed for the NetBSD Project by *Genetec Corporation. * 4. The name of Genetec Corporation may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * IRQ handler for the Agere Vx115 processor. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: vx115_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> #include <machine/bus.h> #include <machine/intr.h> #include <machine/lock.h> #include <arm/vx115/vx115_reg.h> #include <arm/vx115/vx115_var.h> #include <arm/vx115/vx115_intr.h> #define DEBUG_INTR #ifdef DEBUG_INTR #define DPRINTF(fmt...) printf(fmt) static void DPRINTF_MASKS(void) { int i; printf("pri hard mask soft mask

"); for (i = 0; i < NIPL; i++) printf(" %i 0x%08x 0x%08x

", i, vx115_pic_spl_mask[i], vx115_pic_spl_soft_mask[i]); } #else #define DPRINTF(fmt...) #define DPRINTF_MASKS() #endif struct vx115_pic_softc vx115_pic_init_sc; struct vx115_pic_softc *vx115_pic_sc; /* number of hardware IRQs */ #define NR_IRQS 32 /* Array mapping priorities to associated IRQ number */ /* The [IRQ_XXX_PRIORITY] values are defined in vx115_irq.h, and can */ /* be changed without needing to change this array initialization */ /* Note that these priority levels differ from the system priority */ /* levels (SPLs), and are used just to prioritize between simultaneous */ /* interrupts at the controller. */ static u_int32_t irq_priorities[NR_IRQS] = { 0, [IRQ_TIMER1_PRIORITY] = IRQ_TIMER1, [IRQ_DMA1_ERROR_PRIORITY] = IRQ_DMA1_ERROR, [IRQ_DMA1_PRIORITY] = IRQ_DMA1, [IRQ_DISPLAY_PRIORITY] = IRQ_DISPLAY, [IRQ_DISP_SYNC_PRIORITY] = IRQ_DISP_SYNC, [IRQ_SSP1_PRIORITY] = IRQ_SSP1, [IRQ_SSP3_PRIORITY] = IRQ_SSP3, [IRQ_UART0_WAKE_PRIORITY] = IRQ_UART0_WAKE, [IRQ_UART0_PRIORITY] = IRQ_UART0, [IRQ_SDMCC0_PRIORITY] = IRQ_SDMCC0, [IRQ_SDMCC1_PRIORITY] = IRQ_SDMCC1, [IRQ_I2C_PRIORITY] = IRQ_I2C, [IRQ_KEYPAD_PRIORITY] = IRQ_KEYPAD, [IRQ_GPIOA_PRIORITY] = IRQ_GPIOA, [IRQ_GPIOB_PRIORITY] = IRQ_GPIOB, [IRQ_USB_EXTINT_PRIORITY] = IRQ_USB_EXTINT, [IRQ_USB_OTG_PRIORITY] = IRQ_USB_OTG, [IRQ_DSP_PCU_PRIORITY] = IRQ_DSP_PCU, [IRQ_ARM7_PCU_PRIORITY] = IRQ_ARM7_PCU, [IRQ_SWI_PRIORITY] = IRQ_SWI, [IRQ_CAMERA_PRIORITY] = IRQ_CAMERA, [IRQ_EXT2_PRIORITY] = IRQ_EXT2, [IRQ_EXT3_PRIORITY] = IRQ_EXT3, [IRQ_AGPIOA_PRIORITY] = IRQ_AGPIOA, [IRQ_AGPIOB_PRIORITY] = IRQ_AGPIOB, [IRQ_EXT4_PRIORITY] = IRQ_EXT4, [IRQ_EXT5_PRIORITY] = IRQ_EXT5, [IRQ_EXT6_PRIORITY] = IRQ_EXT6, [IRQ_EXT7_PRIORITY] = IRQ_EXT7, [IRQ_RESERVED24_PRIORITY] = IRQ_RESERVED24, [IRQ_RESERVED27_PRIORITY] = IRQ_RESERVED27 }; /* * PIC autoconf glue */ static int vx115_pic_match(struct device *, struct cfdata *, void *); static void vx115_pic_attach(struct device *, struct device *, void *); CFATTACH_DECL(vx115_pic, sizeof(struct vx115_pic_softc), vx115_pic_match, vx115_pic_attach, NULL, NULL); static int vx115_pic_attached; static int stray_interrupt(void *); static void vx115_pic_init_interrupt_masks(void); /* * interrupt dispatch table. */ #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ struct intrhand { TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ int (*ih_func)(void *); /* handler */ void *ih_arg; /* arg for handler */ }; #endif static struct { #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ TAILQ_HEAD(,intrhand) list; #else vx115_irq_handler_t func; #endif void *cookie;/* NULL for stackframe */ /* struct evbnt ev; */ } handler[NR_IRQS]; __volatile int softint_pending; __volatile int current_spl_level; /* interrupt enable masks for each level; used for spl changes */ /* we use separate masks for the hardware and software IRQs, */ /* since the hardware IRQs use 32 bits */ int vx115_pic_spl_mask[NIPL]; int vx115_pic_spl_soft_mask[NIPL]; static int extirq_level[NR_IRQS]; static int vx115_pic_match(struct device *parent, struct cfdata *cf, void *aux) { struct vx115_attach_args *sa = aux; DPRINTF("vx115_pic_match

"); if (vx115_pic_attached || sa->sa_addr != PIC1_BASE_PHYS) return (0); return (1); } void vx115_pic_attach(struct device *parent, struct device *self, void *aux) { struct vx115_pic_softc *sc = (struct vx115_pic_softc*)self; struct vx115_attach_args *sa = (struct vx115_attach_args*)aux; int i; DPRINTF("vx115_pic_attach

"); /* get bus space and handle */ sc->sc_iot = sa->sa_iot; if (vx115_pic_sc == NULL) vx115_pic_sc = sc; /* map bus space and get handle */ if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh) != 0) panic("%s: Cannot map registers", self->dv_xname); vx115_pic_attached = 1; /* disable all interrupts */ vx115_write_pic(PIC_ENABLE_CLEAR, INT_REQ_SRC_CLR_ALL); /* clear all interrupts */ vx115_write_pic(PIC_SOURCE_CLEAR, INT_REQ_SRC_CLR_ALL); /* set up controller priority assignment registers */ for (i = 1; i < NR_IRQS; i++) { /* set the priority control register to the associate IRQ number */ vx115_write_pic(PIC_IPCR_1 + ((i-1)<<2), irq_priorities[i]); } /* enable all irq priority levels, but clear FRZ bit */ vx115_write_pic(PIC_PRIORITY_ENABLE_SET, PIC_IPE_ALL); vx115_write_pic(PIC_PRIORITY_ENABLE_CLEAR, PIC_IPE_E0); /* set all handlers initially to dummy handler */ for(i = 0; i < sizeof handler / sizeof handler[0]; ++i) { handler[i].func = stray_interrupt; handler[i].cookie = (void *)(intptr_t) i; extirq_level[i] = IPL_SERIAL; } vx115_pic_init_interrupt_masks(); _splraise(IPL_SERIAL); enable_interrupts(I32_bit); } /* * Invoked very early on from the board-specific initarm(), in order to * allow us to set up softc pointer, which is needed for spl functions. */ void vx115_intr_bootstrap(bus_addr_t addr, bus_size_t size) { /* set up inital softc struct; this will be used only until device formally attached */ /* assign bus tag: standard vx115 bus ops */ vx115_pic_init_sc.sc_iot = &vx115_bs_tag; /* map bus space and set handle */ if (bus_space_map(vx115_pic_init_sc.sc_iot, addr, size, 0, &vx115_pic_init_sc.sc_ioh) != 0) panic("%s: Cannot map initial interrupt softc region", "vx115_intr_bootstrap"); /* assign softc pointer to init softc struct */ vx115_pic_sc = & vx115_pic_init_sc; } static __inline void __raise(int ipl) { if (current_spl_level < ipl) vx115_setipl(ipl); } /* * Map a software interrupt queue to an interrupt priority level. */ static const int si_to_ipl[SI_NQUEUES] = { IPL_SOFT, /* SI_SOFT */ IPL_SOFTCLOCK, /* SI_SOFTCLOCK */ IPL_SOFTNET, /* SI_SOFTNET */ IPL_SOFTSERIAL, /* SI_SOFTSERIAL */ }; /* * called from irq_entry. */ void vx115_irq_dispatcher(void *arg) { struct clockframe *frame = arg; uint32_t irqbits; int irqno; int saved_spl_level; saved_spl_level = current_spl_level; while ((irqbits = vx115_read_pic(PIC_STATUS)) != 0) { /* FOR LATER: handle IRQs in priority order - the PIC does this for us */ irqno = find_first_bit(irqbits); /* raise spl if necessary to stop interrupts of lower priorities */ if (saved_spl_level < extirq_level[irqno]) vx115_setipl(extirq_level[irqno]); (*handler[irqno].func)( (handler[irqno].cookie == 0) ? frame : handler[irqno].cookie ); /* restore spl if it was changed */ if (saved_spl_level < extirq_level[irqno]) vx115_setipl(saved_spl_level); /* clear interrupt */ vx115_write_pic(PIC_SOURCE_CLEAR, (1<<irqno)); } /* do any pending soft interrupts */ if(softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) vx115_do_pending(); } static int stray_interrupt(void *cookie) { int irqno = (int)cookie; printf("stray interrupt %d

", irqno); if (irqno < NR_IRQS) { /* disable this interrupt */ int save = disable_interrupts(I32_bit); vx115_write_pic(PIC_ENABLE_CLEAR, ~(1U<<irqno)); restore_interrupts(save); } return 0; } /* * Update priority masks for new interrupt at specified priority level. */ void vx115_update_intr_masks(int irqno, int level) { int mask; int *mask_array; int psw = disable_interrupts(I32_bit); int i; /* determine whether hard or soft interrupt, and set mask */ /* and array affected accordingly (hard irq's are < 32) */ if (irqno < 32) { /* hard interrupt */ mask_array = vx115_pic_spl_mask; mask = 1U << irqno; } else { mask_array = vx115_pic_spl_soft_mask; mask = 1U << (irqno - 32); } /* Enable interrupt when at lower priority level */ for(i = 0; i < level; ++i) mask_array[i] |= mask; /* Disable interrupt when at supplied or higher priority level */ for( ; i < NIPL-1; ++i) mask_array[i] &= ~mask; /* * Enforce a heirarchy that gives "slow" device (or devices with * limited input buffer space/"real-time" requirements) a better * chance at not dropping data. */ mask_array[IPL_BIO] &= mask_array[IPL_SOFTNET]; mask_array[IPL_NET] &= mask_array[IPL_BIO]; mask_array[IPL_SOFTSERIAL] &= mask_array[IPL_NET]; mask_array[IPL_TTY] &= mask_array[IPL_SOFTSERIAL]; /* * splvm() blocks all interrupts that use the kernel memory * allocation facilities. */ mask_array[IPL_VM] &= mask_array[IPL_TTY]; /* * Audio devices are not allowed to perform memory allocation * in their interrupt routines, and they have fairly "real-time" * requirements, so give them a high interrupt priority. */ mask_array[IPL_AUDIO] &= mask_array[IPL_VM]; /* * splclock() must block anything that uses the scheduler. */ mask_array[IPL_CLOCK] &= mask_array[IPL_AUDIO]; /* * splhigh() must block "everything". */ mask_array[IPL_HIGH] &= mask_array[IPL_STATCLOCK]; /* * XXX We need serial drivers to run at the absolute highest priority * in order to avoid overruns, so serial > high. */ mask_array[IPL_SERIAL] &= mask_array[IPL_HIGH]; /* enable hardware interrupts appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]); /* disable hardware interrupts appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]); restore_interrupts(psw); DPRINTF("vx115_update_intr_masks: irqno %i, level %i

", irqno, level); DPRINTF_MASKS(); } static void vx115_pic_init_interrupt_masks(void) { DPRINTF("vx115_pic_init_intr_masks

"); memset(vx115_pic_spl_mask, 0, sizeof(vx115_pic_spl_mask)); memset(vx115_pic_spl_soft_mask, 0, sizeof(vx115_pic_spl_soft_mask)); /* * IPL_NONE has soft interrupts enabled only, at least until * hardware handlers are installed. */ vx115_pic_spl_soft_mask[IPL_NONE] = SI_TO_IRQBIT(SI_SOFT) | SI_TO_IRQBIT(SI_SOFTCLOCK) | SI_TO_IRQBIT(SI_SOFTNET) | SI_TO_IRQBIT(SI_SOFTSERIAL); /* * Initialize the soft interrupt masks to block themselves. */ vx115_pic_spl_soft_mask[IPL_SOFT] = ~SI_TO_IRQBIT(SI_SOFT); vx115_pic_spl_soft_mask[IPL_SOFTCLOCK] = ~SI_TO_IRQBIT(SI_SOFTCLOCK); vx115_pic_spl_soft_mask[IPL_SOFTNET] = ~SI_TO_IRQBIT(SI_SOFTNET); vx115_pic_spl_soft_mask[IPL_SOFTSERIAL] = ~SI_TO_IRQBIT(SI_SOFTSERIAL); vx115_pic_spl_soft_mask[IPL_SOFT] &= vx115_pic_spl_soft_mask[IPL_NONE]; /* * splsoftclock() is the only interface that users of the * generic software interrupt facility have to block their * soft intrs, so splsoftclock() must also block IPL_SOFT. */ vx115_pic_spl_soft_mask[IPL_SOFTCLOCK] &= vx115_pic_spl_soft_mask[IPL_SOFT]; /* * splsoftnet() must also block splsoftclock(), since we don't * want timer-driven network events to occur while we're * processing incoming packets. */ vx115_pic_spl_soft_mask[IPL_SOFTNET] &= vx115_pic_spl_soft_mask[IPL_SOFTCLOCK]; DPRINTF_MASKS(); } void vx115_do_pending(void) { static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED; int oldirqstate, spl_save; if (__cpu_simple_lock_try(&processing) == 0) return; spl_save = current_spl_level; oldirqstate = disable_interrupts(I32_bit); #define DO_SOFTINT(si,ipl) \ if ((softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) & SI_TO_IRQBIT(si)) { \ softint_pending &= ~SI_TO_IRQBIT(si); \ __raise(ipl); \ restore_interrupts(oldirqstate); \ softintr_dispatch(si); \ oldirqstate = disable_interrupts(I32_bit); \ vx115_setipl(spl_save); \ } do { DO_SOFTINT(SI_SOFTSERIAL,IPL_SOFTSERIAL); DO_SOFTINT(SI_SOFTNET, IPL_SOFTNET); DO_SOFTINT(SI_SOFTCLOCK, IPL_SOFTCLOCK); DO_SOFTINT(SI_SOFT, IPL_SOFT); } while( softint_pending & vx115_pic_spl_soft_mask[current_spl_level] ); __cpu_simple_unlock(&processing); restore_interrupts(oldirqstate); } #undef splx void splx(int ipl) { vx115_splx(ipl); } #undef _splraise int _splraise(int ipl) { return vx115_splraise(ipl); } #undef _spllower int _spllower(int ipl) { return vx115_spllower(ipl); } #undef _setsoftintr void _setsoftintr(int si) { return vx115_setsoftintr(si); } void * vx115_intr_establish(int irqno, int level, int (*func)(void *), void *cookie) { int psw; if (irqno < VX115_IRQ_MIN || irqno >= VX115_IRQ_MAX) panic("intr_establish: bogus irq number %d", irqno); psw = disable_interrupts(I32_bit); handler[irqno].cookie = cookie; handler[irqno].func = func; extirq_level[irqno] = level; vx115_update_intr_masks(irqno, level); restore_interrupts(psw); return (&handler[irqno]); } /* Configure the irq's sense (level or edge) and polarity (low-high or high-low) */ int vx115_configure_irq(unsigned int irq, unsigned int sense, unsigned int polarity) { unsigned int irq_mask; unsigned int irq_enabled; unsigned int offset; /* world's most annoying register offset calculation */ offset = irq << 2; if ((1 <= irq) && (irq <= 7)) { offset += 0xA4; } else if ((8 <= irq) && (irq <= 26)) { offset += 0xE0; } else if ((27 <= irq) && (irq <= 28)) { offset += 0x58; } else if ((29 <= irq) && (irq <= 30)) { offset += 0x64; } else { /* bad irq number supplied */ return -1; } /* create mask for this irq */ irq_mask = 1 << irq; /* get current interrupt enabled state */ irq_enabled = vx115_read_pic(PIC_ENABLE_SET) & irq_mask; /* assign supplied parameters */ vx115_write_pic(offset, sense | polarity | VX115_INT_ENABLED); /* clear the interrupt in case got a false one */ vx115_write_pic(PIC_SOURCE_CLEAR, irq_mask); /* reenable the irq if enabled on entry */ vx115_write_pic(PIC_ENABLE_SET, irq_enabled); return 0; } void vx115_enable_irq(unsigned int irq) { vx115_write_pic(PIC_ENABLE_SET, 1 << irq); } void vx115_disable_irq(unsigned int irq) { vx115_write_pic(PIC_ENABLE_CLEAR, 1 << irq); }