/* $NetBSD: $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Takuya SHIOZAKI. * * 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. */ #include __KERNEL_RCSID(0, "$NetBSD: $"); #include #include #include #include #include #include #include #include /* for PAGE_SIZE */ #include #include #include #define KEIROMEO_BASE_ADDR_REG0 0x10 #define KEIROMEO_BASE_ADDR_REG1 0x14 #define KEIROMEO_BAR1_YM2151_ADDR 0x0000 #define KEIROMEO_BAR1_YM2151_DATA 0x0004 #define KEIROMEO_BAR1_YM2151_SNOOP 0x0010 #define KEIROMEO_BAR1_YM2151_CQUEUE 0x0018 #define KEIROMEO_BAR1_YM2151_CTRL 0x001C #define KEIROMEO_BAR1_YMF288_REG0 0x0100 #define KEIROMEO_BAR1_YMF288_REG1 0x0104 #define KEIROMEO_BAR1_YMF288_REG2 0x0108 #define KEIROMEO_BAR1_YMF288_REG3 0x010C #define KEIROMEO_BAR1_YMF288_CTRL 0x011C #define YM2151_S_BUSY 0x80 #define YM2151_RETRY 256 struct keiromeo_softc { struct device sc_dev; /* */ int sc_ymf288_present; int sc_ym2151_present; /* */ pci_intr_handle_t *sc_ih; bus_space_tag_t sc_snoopt; bus_space_handle_t sc_snooph; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; }; static int keiromeo_match(struct device *, struct cfdata *, void *); static void keiromeo_attach(struct device *, struct device *, void *); static int keiromeo_intr(void *); struct cfattach keiromeo_ca = { sizeof(struct keiromeo_softc), keiromeo_match, keiromeo_attach }; /* * handle the space pointed by Base Address Register 1 */ static __inline void bar1_write_1(struct keiromeo_softc *sc, bus_size_t offset, u_int32_t value) { bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, value); } static __inline u_int32_t bar1_read_1(struct keiromeo_softc *sc, bus_size_t offset) { return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, offset)); } static __inline void bar1_write_2(struct keiromeo_softc *sc, bus_size_t offset, u_int32_t value) { bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, value); } static __inline u_int32_t bar1_read_2(struct keiromeo_softc *sc, bus_size_t offset) { return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset)); } static __inline void bar1_write_4(struct keiromeo_softc *sc, bus_size_t offset, u_int32_t value) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value); } static __inline u_int32_t bar1_read_4(struct keiromeo_softc *sc, bus_size_t offset) { return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset)); } /* * YM2151 stuffs */ /* KEI-Chan queue status */ static __inline int ym2151_queue_used(struct keiromeo_softc *sc) { return (0x0F-(bar1_read_1(sc, KEIROMEO_BAR1_YM2151_CQUEUE) & 0x0F)); } static __inline int ym2151_queue_empty(struct keiromeo_softc *sc) { return (ym2151_queue_used(sc)==0); } static __inline int ym2151_queue_full(struct keiromeo_softc *sc) { return (ym2151_queue_used(sc)==0x0F); } /* read YM2151 status */ static __inline u_int8_t ym2151_read_status(struct keiromeo_softc *sc) { return (bar1_read_1(sc, KEIROMEO_BAR1_YM2151_DATA)); } /* whether YM2151 is busy */ static __inline int ym2151_busy(struct keiromeo_softc *sc) { return (ym2151_read_status(sc) & YM2151_S_BUSY); } /* wait for ack of all writing */ static __inline int ym2151_sync(struct keiromeo_softc *sc) { int i; for (i=0; i>24)&0xFF); } /* read GPIO status */ static __inline u_int8_t ym2151_read_gpio_status(struct keiromeo_softc *sc) { return ((bar1_read_4(sc, KEIROMEO_BAR1_YM2151_ADDR)>>16)&0xFF); } /* * probe FM synthesizers. */ static __inline int is_ymf288_present(struct keiromeo_softc *sc) { bar1_write_4(sc, KEIROMEO_BAR1_YMF288_CTRL, 0); /* reset */ if ((bar1_read_4(sc, KEIROMEO_BAR1_YMF288_REG0) & 0xFF)!=0xFF) { printf("%s: YMF288 absent. (reg0 != 0xFF while resetting)\n", sc->sc_dev.dv_xname); return (0); } bar1_write_4(sc, KEIROMEO_BAR1_YMF288_CTRL, 0x80); /* clear reset */ if ((bar1_read_4(sc, KEIROMEO_BAR1_YMF288_REG0) & 0xFF)==0xFF) { printf("%s: YMF288 absent. (reg0 == 0xFF just after reset)\n", sc->sc_dev.dv_xname); return (0); } printf("%s: YMF288 ok.\n", sc->sc_dev.dv_xname); return (1); } static __inline int is_ym2151_present(struct keiromeo_softc *sc) { /* stage1: check efficiency of reset for ym2151. */ bar1_write_4(sc, KEIROMEO_BAR1_YM2151_CTRL, 0); /* reset */ delay(1000); if (bar1_read_1(sc, KEIROMEO_BAR1_YM2151_DATA) != 0xFF) { printf("%s: YM2151 absent. (data != 0xFF while resetting)\n", sc->sc_dev.dv_xname); return (0); } bar1_write_1(sc, KEIROMEO_BAR1_YM2151_CTRL, 0x80); /* clear reset */ delay(1000); if (bar1_read_1(sc, KEIROMEO_BAR1_YM2151_DATA) ==0xFF) { printf("%s: YM2151 absent. (data == 0xFF just after reset)\n", sc->sc_dev.dv_xname); return (0); } /* stage2: sanity check of CT1/CT2 port. */ /* CT1/CT2 on */ if (ym2151_write_reg(sc, 0x1B, 0xC0)) { printf("%s: YM2151 error. (write reg failed)\n", sc->sc_dev.dv_xname); return (0); } if (ym2151_sync(sc)) { printf("%s: YM2151 error. (flush queue time out)\n", sc->sc_dev.dv_xname); return (0); } delay(1000); if ((ym2151_read_gpio_status(sc) & 0xC0) != 0xC0) { printf("%s: YM2151 error. (inconsist CT1/CT2) stat=0x%02X\n", sc->sc_dev.dv_xname, ym2151_read_gpio_status(sc)); return (0); } /* CT1/CT2 off */ if (ym2151_write_reg(sc, 0x1B, 0x00)) { printf("%s: YM2151 error. (write reg failed)\n", sc->sc_dev.dv_xname); return (0); } if (ym2151_sync(sc)) { printf("%s: YM2151 error. (flush queue time out)\n", sc->sc_dev.dv_xname); return (0); } delay(1000); if ((ym2151_read_gpio_status(sc) & 0xC0) != 0x00) { printf("%s: YM2151 error. (inconsist CT1/CT2) stat=0x%02X\n", sc->sc_dev.dv_xname, ym2151_read_gpio_status(sc)); return (0); } printf("%s: YM2151 ok.\n", sc->sc_dev.dv_xname); return (1); } /* * */ static int /*ARGSUSED*/ keiromeo_match(struct device *parent, struct cfdata *match, void *aux) { struct pci_attach_args *pa = aux; if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_KURUSUGAWA && PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_KURUSUGAWA_ROMEO) return (1); return (0); } static void /*ARGSUSED*/ keiromeo_attach(struct device *parent, struct device *self, void *aux) { struct keiromeo_softc *sc = (struct keiromeo_softc *)self; struct pci_attach_args *pa = aux; char devinfo[256]; char const *strintr; pci_intr_handle_t ih; pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo); printf(": %s (rev. 0x%02x)\n", devinfo, PCI_REVISION(pa->pa_class)); sc->sc_ymf288_present = 0; sc->sc_ym2151_present = 0; /* map bus space */ if (pci_mapreg_map(pa, KEIROMEO_BASE_ADDR_REG0, PCI_MAPREG_TYPE_MEM, 0, &sc->sc_snoopt, &sc->sc_snooph, NULL, NULL)) { printf("%s: failed to map snoop space\n", sc->sc_dev.dv_xname); return; } if (pci_mapreg_map(pa, KEIROMEO_BASE_ADDR_REG1, PCI_MAPREG_TYPE_MEM, 0, &sc->sc_iot, &sc->sc_ioh, NULL, NULL)) { printf("%s: failed to map control space\n", sc->sc_dev.dv_xname); return; } /* interrupt */ if (pci_intr_map(pa, &ih)) { printf("%s: failed to map interrupt\n", sc->sc_dev.dv_xname); return; } strintr = pci_intr_string(pa->pa_pc, ih); sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, keiromeo_intr, sc); if (sc->sc_ih == NULL) { printf("%s: failed to establish interrupt", sc->sc_dev.dv_xname); if (strintr != NULL) printf(" at %s", strintr); printf("\n"); return; } printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, strintr); if (is_ymf288_present(sc)) { sc->sc_ymf288_present = 1; } else { sc->sc_ymf288_present = 0; } if (is_ym2151_present(sc)) { sc->sc_ym2151_present = 1; } else { sc->sc_ym2151_present = 0; } } static int /*ARGSUSED*/ keiromeo_intr(void *arg) { return (0); }