diff options
Diffstat (limited to 'sys/arch/aviion/dev/vme.c')
-rw-r--r-- | sys/arch/aviion/dev/vme.c | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/sys/arch/aviion/dev/vme.c b/sys/arch/aviion/dev/vme.c new file mode 100644 index 00000000000..514150dc19b --- /dev/null +++ b/sys/arch/aviion/dev/vme.c @@ -0,0 +1,550 @@ +/* $OpenBSD: vme.c,v 1.1 2006/05/09 18:14:15 miod Exp $ */ +/* + * Copyright (c) 2006, Miodrag Vallat. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * XXX TODO: Finish /dev/vme{a16,a24,a32}{d8,d16,d32} interface. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/extent.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/uio.h> + +#include <machine/bus.h> +#include <machine/conf.h> + +#include <uvm/uvm_extern.h> + +#include <aviion/dev/vmevar.h> + +#include <machine/av400.h> +#include <aviion/dev/sysconreg.h> + +struct vmesoftc { + struct device sc_dev; + + struct extent *sc_ext_a16; + struct extent *sc_ext_a24; + struct extent *sc_ext_a32; +}; + +int vmematch(struct device *, void *, void *); +void vmeattach(struct device *, struct device *, void *); + +struct cfattach vme_ca = { + sizeof(struct vmesoftc), vmematch, vmeattach +}; + +struct cfdriver vme_cd = { + NULL, "vme", DV_DULL +}; + +int vme16_map(bus_addr_t, bus_size_t, int, bus_space_handle_t *); +void vme16_unmap(bus_space_handle_t, bus_size_t); +void * vme16_vaddr(bus_space_handle_t); +int vme24_map(bus_addr_t, bus_size_t, int, bus_space_handle_t *); +void vme24_unmap(bus_space_handle_t, bus_size_t); +void * vme24_vaddr(bus_space_handle_t); +int vme32_map(bus_addr_t, bus_size_t, int, bus_space_handle_t *); +void vme32_unmap(bus_space_handle_t, bus_size_t); +void * vme32_vaddr(bus_space_handle_t); +int vme_subregion(bus_space_handle_t, bus_size_t, bus_size_t, + bus_space_handle_t *); + +int vme_map(struct extent *, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +void vme_unmap(struct extent *, bus_space_handle_t, bus_size_t); +int vmeprint(void *, const char *); +int vmescan(struct device *, void *, void *); + +u_int vmevecbase; + +int +vmematch(struct device *parent, void *vcf, void *aux) +{ + /* XXX no VME on AV100/AV200/AV300, though */ + return (1); +} + +void +vmeattach(struct device *parent, struct device *self, void *aux) +{ + struct vmesoftc *sc = (struct vmesoftc *)self; + u_int32_t ucsr; + + printf("\n"); + + /* + * Initialize extents + */ + sc->sc_ext_a16 = extent_create("vme a16", 0, 1 << (16 - PAGE_SHIFT), + M_DEVBUF, NULL, 0, EX_NOWAIT); + sc->sc_ext_a24 = extent_create("vme a24", 0, 1 << (24 - PAGE_SHIFT), + M_DEVBUF, NULL, 0, EX_NOWAIT); + sc->sc_ext_a32 = extent_create("vme a32", 0, 1 << (32 - PAGE_SHIFT), + M_DEVBUF, NULL, 0, EX_NOWAIT); + + vmevecbase = 0x80; /* Hard coded for AV400 */ + + /* + * Force a reasonable timeout for VME data transfers. + * We can not disable this, this would cause autoconf to hang + * on the first missing device we'll probe. + */ + ucsr = *(volatile u_int32_t*)AV400_UCSR; + ucsr = (ucsr & ~VTOSELBITS) | VTO128US; + *(volatile u_int32_t *)AV400_UCSR = ucsr; + + /* + * Clear EXTAD to allow VME A24 devices to access the first 16MB + * of memory. + */ + *(volatile u_int32_t *)AV400_EXTAD = 0x00000000; + + /* + * Use supervisor data address modifiers for VME accesses. + */ + *(volatile u_int32_t *)AV400_EXTAM = 0x0d; + + /* + * Display AV400 VME ranges. + */ + printf("%s: A32 %08x-%08x\n", self->dv_xname, + AV400_VME32_START1, AV400_VME32_END1); + printf("%s: A32 %08x-%08x\n", self->dv_xname, + AV400_VME32_START2, AV400_VME32_END2); + printf("%s: A24 %08x-%08x\n", self->dv_xname, + AV400_VME24_START, AV400_VME24_END); + printf("%s: A16 %08x-%08x\n", self->dv_xname, + AV400_VME16_START, AV400_VME16_END); + + /* scan for child devices */ + config_search(vmescan, self, aux); +} + +int +vmescan(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = vcf; + struct vme_attach_args vaa; + + bzero(&vaa, sizeof vaa); + vaa.vaa_addr_a16 = (vme_addr_t)cf->cf_loc[0]; + vaa.vaa_addr_a24 = (vme_addr_t)cf->cf_loc[1]; + vaa.vaa_addr_a32 = (vme_addr_t)cf->cf_loc[2]; + vaa.vaa_ipl = (u_int)cf->cf_loc[3]; + + if ((*cf->cf_attach->ca_match)(parent, cf, &vaa) == 0) + return (0); + + config_attach(parent, cf, &vaa, vmeprint); + return (1); +} + +int +vmeprint(void *aux, const char *pnp) +{ + struct vme_attach_args *vaa = aux; + + if (vaa->vaa_addr_a16 != (vme_addr_t)-1) + printf(" a16 0x%04x", vaa->vaa_addr_a16); + if (vaa->vaa_addr_a24 != (vme_addr_t)-1) + printf(" a24 0x%06x", vaa->vaa_addr_a24); + if (vaa->vaa_addr_a32 != (vme_addr_t)-1) + printf(" a32 0x%08x", vaa->vaa_addr_a32); + if (vaa->vaa_ipl != (u_int)-1) + printf(" ipl %u", vaa->vaa_ipl); + + return (UNCONF); +} + +/* + * Interrupt related code + */ + +/* allocate interrupt vectors */ +int +vmeintr_allocate(u_int count, int flags, u_int *array) +{ + u_int vec, v; + + if ((flags & VMEINTR_CONTIGUOUS) == 0) { + for (vec = vmevecbase; vec <= NVMEINTR - count; vec++) { + if (SLIST_EMPTY(&intr_handlers[vec])) { + *array++ = vec; + if (--count == 0) + return (0); + } + } + } else { + for (vec = vmevecbase; vec <= NVMEINTR - count; vec++) { + /* do we have count contiguous unassigned vectors? */ + for (v = count; v != 0; v--) + if (!SLIST_EMPTY(&intr_handlers[vec + v - 1])) + break; + + if (v == 0) { + *array = vec; + return (0); + } + } + } + + return (EPERM); +} + +/* enable and establish interrupt */ +int +vmeintr_establish(u_int vec, struct intrhand *ih, const char *name) +{ + /* + * No need to enable the VME interrupt source in the AV400 interrupt + * controller, as they are enabled by default. + */ + return intr_establish(vec, ih, name); +} + +/* + * bus_space specific functions + */ + +int +vme16_map(bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *ret) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + if (AV400_ISVMEA32(addr) && AV400_ISVMEA32(addr + size - 1)) + return (vme_map(sc->sc_ext_a16, addr, size, flags, ret)); + else + return (EINVAL); +} + +int +vme24_map(bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *ret) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + if (AV400_ISVMEA24(addr) && AV400_ISVMEA24(addr + size - 1)) + return (vme_map(sc->sc_ext_a24, addr, size, flags, ret)); + else + return (EINVAL); +} + +int +vme32_map(bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *ret) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + if (AV400_ISVMEA16(addr) && AV400_ISVMEA16(addr + size - 1)) + return (vme_map(sc->sc_ext_a32, addr, size, flags, ret)); + else + return (EINVAL); +} + +int +vme_map(struct extent *ext, bus_addr_t addr, bus_size_t size, int flags, + bus_space_handle_t *ret) +{ + int rc; + paddr_t pa; + psize_t len; + vaddr_t ova, va; + u_int pg; + extern vaddr_t pmap_map(vaddr_t, paddr_t, paddr_t, vm_prot_t, u_int); + + pa = trunc_page((paddr_t)addr); + len = round_page((paddr_t)addr + size) - pa; + + if (ext != NULL) { + rc = extent_alloc_region(ext, atop(pa), atop(len), + EX_NOWAIT | EX_MALLOCOK); + if (rc != 0) + return (rc); + } + + ova = va = uvm_km_valloc(kernel_map, len); + if (va == NULL) { + rc = ENOMEM; + goto fail; + } + + *ret = (bus_space_handle_t)va; + + for (pg = atop(len); pg !=0; pg--) { + pmap_kenter_pa(va, pa, UVM_PROT_RW); + va += PAGE_SIZE; + pa += PAGE_SIZE; + } + if (flags & BUS_SPACE_MAP_CACHEABLE) + pmap_cache_ctrl(pmap_kernel(), ova, ova + len, CACHE_GLOBAL); + pmap_update(pmap_kernel()); + + return (0); + +fail: + if (ext != NULL) + extent_free(ext, atop(pa), atop(len), EX_NOWAIT | EX_MALLOCOK); + return (rc); +} + +void +vme16_unmap(bus_space_handle_t handle, bus_size_t size) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + return (vme_unmap(sc->sc_ext_a16, handle, size)); +} + +void +vme24_unmap(bus_space_handle_t handle, bus_size_t size) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + return (vme_unmap(sc->sc_ext_a24, handle, size)); +} + +void +vme32_unmap(bus_space_handle_t handle, bus_size_t size) +{ + struct vmesoftc *sc = (void *)vme_cd.cd_devs[0]; + + return (vme_unmap(sc->sc_ext_a32, handle, size)); +} + +void +vme_unmap(struct extent *ext, bus_space_handle_t handle, bus_size_t size) +{ + vaddr_t va; + vsize_t len; + + va = trunc_page((vaddr_t)handle); + len = round_page((vaddr_t)handle + size) - va; + + pmap_kremove(va, len); + pmap_update(pmap_kernel()); + uvm_km_free(kernel_map, va, len); + + if (ext != NULL) + extent_free(ext, atop(va), atop(len), EX_NOWAIT | EX_MALLOCOK); +} + +int +vme_subregion(bus_space_handle_t handle, bus_addr_t offset, bus_size_t size, + bus_space_handle_t *ret) +{ + /* since vme_map produces linear mappings, this is safe */ + *ret = handle + offset; + return (0); +} + +void * +vme16_vaddr(bus_space_handle_t handle) +{ + return (void *)(AV400_VME16_START + (vaddr_t)handle); +} + +void * +vme24_vaddr(bus_space_handle_t handle) +{ + return (void *)(AV400_VME24_START + (vaddr_t)handle); +} + +void * +vme32_vaddr(bus_space_handle_t handle) +{ + return (void *)(handle); +} + +/* + * Get a bus_space_tag for the requested address and data access modes. + * + * On aviion, we do not honour the dspace yet. + */ +int +vmebus_get_bst(struct device *vsc, u_int aspace, u_int dspace, + bus_space_tag_t *bst) +{ + struct aviion_bus_space_tag *tag; + + switch (dspace) { + case VME_D32: + case VME_D16: + case VME_D8: + break; + default: + return (EINVAL); + } + + switch (aspace) { + case VME_A32: + case VME_A24: + case VME_A16: + break; + default: + return (EINVAL); + } + + tag = (struct aviion_bus_space_tag *)malloc(sizeof *tag, M_DEVBUF, + M_NOWAIT); + if (tag == NULL) + return (ENOMEM); + + switch (aspace) { + default: + case VME_A32: + tag->bs_map = vme32_map; + tag->bs_unmap = vme32_unmap; + tag->bs_subregion = vme_subregion; + tag->bs_vaddr = vme32_vaddr; + break; + case VME_A24: + tag->bs_map = vme24_map; + tag->bs_unmap = vme24_unmap; + tag->bs_subregion = vme_subregion; + tag->bs_vaddr = vme24_vaddr; + break; + case VME_A16: + tag->bs_map = vme16_map; + tag->bs_unmap = vme16_unmap; + tag->bs_subregion = vme_subregion; + tag->bs_vaddr = vme16_vaddr; + break; + } + + *bst = tag; + return (0); +} + +void +vmebus_release_bst(struct device *vsc, bus_space_tag_t b) +{ + free((void *)b, M_DEVBUF); +} + +/* + * /dev/vme* access routines + */ + +/* minor device number encoding */ +#define AWIDTH_FIELD(minor) (minor & 0x0f) +#define AWIDTH(w) ((w) << 3) +#define DWIDTH_FIELD(minor) ((minor & 0xf0) >> 4) +#define DWIDTH(w) ((w) << 3) + +int +vmeopen(dev_t dev, int flags, int type, struct proc *p) +{ + if (vme_cd.cd_ndevs == 0 || vme_cd.cd_devs[0] == NULL) + return (ENODEV); + + switch (AWIDTH_FIELD(minor(dev))) { + case VME_A32: + case VME_A24: + case VME_A16: + break; + default: + return (ENODEV); + } + + switch (DWIDTH_FIELD(minor(dev))) { + case VME_D32: + case VME_D16: + case VME_D8: + break; + default: + return (ENODEV); + } + + return (0); +} + +int +vmeclose(dev_t dev, int flags, int type, struct proc *p) +{ + return (0); +} + +int +vmeread(dev_t dev, struct uio *uio, int flags) +{ + return (EIO); +} + +int +vmewrite(dev_t dev, struct uio *uio, int flags) +{ + return (EIO); +} + +int +vmeioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + switch (cmd) { + default: + return (ENOTTY); + } +} + +paddr_t +vmemmap(dev_t dev, off_t off, int prot) +{ + int awidth; + paddr_t pa; + + if ((off & PAGE_MASK) != 0) + return (-1); + + awidth = AWIDTH_FIELD(minor(dev)); + + /* check offset range */ + if (off < 0 || off >= (1ULL << AWIDTH(awidth))) + return (-1); + + pa = (paddr_t)off; + + switch (awidth) { + case VME_A32: + if (!AV400_ISVMEA32(pa)) + return (-1); + break; + case VME_A24: + pa += AV400_VME24_START; + if (!AV400_ISVMEA24(pa)) + return (-1); + break; + case VME_A16: + pa += AV400_VME16_START; + if (!AV400_ISVMEA16(pa)) + return (-1); + break; + } + + return (atop(pa)); +} |