diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2006-10-15 19:55:48 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2006-10-15 19:55:48 +0000 |
commit | d27322a7d799ede63b48f1e8190ba4eb6466b199 (patch) | |
tree | f6bcd6a80b541d09e6fcba1e649fe2bfc98e9167 /sys/dev | |
parent | 7237b595f443856244e1571692d4a872b7b26f6c (diff) |
First stubs for a new driver for the Marvell Libertas chips.
The card correctly loads the firmware and it is possible to send a few
simple commands to the card but that's it. No packet are sent or received.
Only the Netgear WG511v2 cardbus card is tested.
A cheese fondue and a bigger amount of white wine was needed to make the
interrupts work -- until last night the driver was uploading the firmware
to fast and the card garbled the image without moaning. It took us a full
day and hundreds of test kernels to figure that out.
Joint work with mglocker@, fondue by mbalmer@
OK mglocker@, get it in deraadt@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cardbus/files.cardbus | 8 | ||||
-rw-r--r-- | sys/dev/cardbus/if_malo_cardbus.c | 230 | ||||
-rw-r--r-- | sys/dev/ic/malo.c | 613 | ||||
-rw-r--r-- | sys/dev/ic/malo.h | 48 |
4 files changed, 898 insertions, 1 deletions
diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus index 754d94a990f..0f4381928d9 100644 --- a/sys/dev/cardbus/files.cardbus +++ b/sys/dev/cardbus/files.cardbus @@ -1,4 +1,4 @@ -# $OpenBSD: files.cardbus,v 1.22 2006/09/28 05:02:33 mglocker Exp $ +# $OpenBSD: files.cardbus,v 1.23 2006/10/15 19:55:47 claudio Exp $ # $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $ # # files.cardbus @@ -111,3 +111,9 @@ file dev/cardbus/ohci_cardbus.c ohci_cardbus # attach uhci at cardbus with uhci_cardbus file dev/cardbus/uhci_cardbus.c uhci_cardbus + +# +# Marvel Libertas Open +# +attach malo at cardbus with malo_cardbus +file dev/cardbus/if_malo_cardbus.c malo_cardbus diff --git a/sys/dev/cardbus/if_malo_cardbus.c b/sys/dev/cardbus/if_malo_cardbus.c new file mode 100644 index 00000000000..4a5abc656cc --- /dev/null +++ b/sys/dev/cardbus/if_malo_cardbus.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/cardbus/cardbusvar.h> + +#include <dev/ic/malo.h> + +struct malo_cardbus_softc { + struct malo_softc sc_malo; + + /* cardbus specific goo */ + cardbus_devfunc_t sc_ct; + cardbustag_t sc_tag; + void *sc_ih; + + bus_size_t sc_mapsize1; + bus_size_t sc_mapsize2; + pcireg_t sc_bar1_val; + pcireg_t sc_bar2_val; + int sc_intrline; +}; + +int malo_cardbus_match(struct device *parent, void *match, void *aux); +void malo_cardbus_attach(struct device *parent, struct device *self, + void *aux); +int malo_cardbus_detach(struct device *self, int flags); +void malo_cardbus_setup(struct malo_cardbus_softc *csc); +int malo_cardbus_enable(struct malo_softc *sc); +void malo_cardbus_disable(struct malo_softc *sc); + +struct cfattach malo_cardbus_ca = { + sizeof (struct malo_cardbus_softc), malo_cardbus_match, + malo_cardbus_attach, malo_cardbus_detach +}; + +static const struct cardbus_matchid malo_cardbus_devices[] = { + { PCI_VENDOR_MARVELL, PCI_PRODUCT_MARVELL_88W8335_1 }, + { PCI_VENDOR_MARVELL, PCI_PRODUCT_MARVELL_88W8335_2 } +}; + +int +malo_cardbus_match(struct device *parent, void *match, void *aux) +{ + return (cardbus_matchbyid(aux, malo_cardbus_devices, + sizeof (malo_cardbus_devices) / sizeof (malo_cardbus_devices[0]))); +} + +void +malo_cardbus_attach(struct device *parent, struct device *self, void *aux) +{ + struct malo_cardbus_softc *csc = (struct malo_cardbus_softc *)self; + struct cardbus_attach_args *ca = aux; + struct malo_softc *sc = &csc->sc_malo; + cardbus_devfunc_t ct = ca->ca_ct; + bus_addr_t base; + int error; + + sc->sc_dmat = ca->ca_dmat; + csc->sc_ct = ct; + csc->sc_tag = ca->ca_tag; + csc->sc_intrline = ca->ca_intrline; + + /* power management hooks */ + sc->sc_enable = malo_cardbus_enable; + sc->sc_disable = malo_cardbus_disable; +#if 0 + sc->sc_power = malo_cardbus_power; +#endif + + /* map control/status registers */ + error = Cardbus_mapreg_map(ct, CARDBUS_BASE0_REG, + CARDBUS_MAPREG_TYPE_MEM, 0, &sc->sc_mem1_bt, + &sc->sc_mem1_bh, &base, &csc->sc_mapsize1); + if (error != 0) { + printf(": could not map 1st memory space\n"); + return; + } + csc->sc_bar1_val = base | CARDBUS_MAPREG_TYPE_MEM; + + /* map control/status registers */ + error = Cardbus_mapreg_map(ct, CARDBUS_BASE1_REG, + CARDBUS_MAPREG_TYPE_MEM, 0, &sc->sc_mem2_bt, + &sc->sc_mem2_bh, &base, &csc->sc_mapsize2); + if (error != 0) { + printf(": could not map 2nd memory space\n"); + Cardbus_mapreg_unmap(ct, CARDBUS_BASE0_REG, sc->sc_mem1_bt, + sc->sc_mem1_bh, csc->sc_mapsize1); + return; + } + csc->sc_bar2_val = base | CARDBUS_MAPREG_TYPE_MEM; + + /* set up the PCI configuration registers */ + malo_cardbus_setup(csc); + + printf(": irq %d", csc->sc_intrline); + + error = malo_attach(sc); + if (error != 0) + malo_cardbus_detach(&sc->sc_dev, 0); + + Cardbus_function_disable(ct); +} + +int +malo_cardbus_detach(struct device *self, int flags) +{ + struct malo_cardbus_softc *csc = (struct malo_cardbus_softc *)self; + struct malo_softc *sc = &csc->sc_malo; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + int error; + + error = malo_detach(sc); + if (error != 0) + return (error); + + /* unhook the interrupt handler */ + if (csc->sc_ih != NULL) { + cardbus_intr_disestablish(cc, cf, csc->sc_ih); + csc->sc_ih = NULL; + } + + /* release bus space and close window */ + Cardbus_mapreg_unmap(ct, CARDBUS_BASE0_REG, sc->sc_mem1_bt, + sc->sc_mem1_bh, csc->sc_mapsize1); + Cardbus_mapreg_unmap(ct, CARDBUS_BASE1_REG, sc->sc_mem2_bt, + sc->sc_mem2_bh, csc->sc_mapsize2); + + return (0); +} + +void +malo_cardbus_setup(struct malo_cardbus_softc *csc) +{ + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + pcireg_t reg; + + /* program the BAR */ + cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_BASE0_REG, + csc->sc_bar1_val); + cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_BASE1_REG, + csc->sc_bar2_val); + + /* make sure the right access type is on the cardbus bridge */ + (*cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE); + (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + + /* enable the appropriate bits in the PCI CSR */ + reg = cardbus_conf_read(cc, cf, csc->sc_tag, + CARDBUS_COMMAND_STATUS_REG); + reg |= CARDBUS_COMMAND_MASTER_ENABLE | CARDBUS_COMMAND_MEM_ENABLE; + cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_COMMAND_STATUS_REG, + reg); +} + +int +malo_cardbus_enable(struct malo_softc *sc) +{ + struct malo_cardbus_softc *csc = (struct malo_cardbus_softc *)sc; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + /* power on the socket */ + Cardbus_function_enable(ct); + + /* setup the PCI configuration registers */ + malo_cardbus_setup(csc); + + /* map and establish the interrupt handler */ + csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline, IPL_NET, + malo_intr, sc, sc->sc_dev.dv_xname); + if (csc->sc_ih == NULL) { + printf("%s: could not establish interrupt at %d\n", + sc->sc_dev.dv_xname, csc->sc_intrline); + Cardbus_function_disable(ct); + return (1); + } + + return (0); +} + +void +malo_cardbus_disable(struct malo_softc *sc) +{ + struct malo_cardbus_softc *csc = (struct malo_cardbus_softc *)sc; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + /* unhook the interrupt handler */ + cardbus_intr_disestablish(cc, cf, csc->sc_ih); + csc->sc_ih = NULL; + + /* power down the socket */ + Cardbus_function_disable(ct); +} diff --git a/sys/dev/ic/malo.c b/sys/dev/ic/malo.c new file mode 100644 index 00000000000..98543a5a756 --- /dev/null +++ b/sys/dev/ic/malo.c @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bpfilter.h" + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/types.h> + +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/endian.h> +//#include <machine/intr.h> + +#include <net/if.h> +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/if_ether.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/ic/malo.h> + +#ifdef MALO_DEBUG +#define DPRINTF(x) do { printf x; } while (0) +#else +#define DPRINTF(x) +#endif + +/* firmware commands as found in a document describing the Libertas FW */ +#define MALO_CMD_GET_HW_SPEC 0x0003 +#define MALO_CMD_RESPONSE 0x8000 + +struct malo_cmdheader { + uint16_t cmd; + uint16_t size; /* size of the command, incl. header */ + uint16_t seqnum; /* seems not to matter that much */ + uint16_t result; /* set to 0 on request */ + /* following the data payload, up to 256 bytes */ +}; + +struct malo_hw_spec { + uint16_t HwVersion; + uint16_t NumOfWCB; /* reserved */ + uint16_t NumOfMCastAdr; + uint8_t PermanentAddress[6]; + uint16_t RegionCode; + uint16_t NumberOfAntenna; + uint32_t FWReleaseNumber; + uint32_t RxPdRd1Ptr; + uint32_t RxPdRd2Ptr; + uint32_t RxPdWrPtr; + uint32_t CookiePtr; +} __packed; + + +#define malo_mem_write4(sc, off, x) \ + bus_space_write_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, (off), (x)) +#define malo_mem_write2(sc, off, x) \ + bus_space_write_2((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, (off), (x)) +#define malo_mem_write1(sc, off, x) \ + bus_space_write_1((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, (off), (x)) + +#define malo_mem_read4(sc, off) \ + bus_space_read_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, (off)) +#define malo_mem_read1(sc, off) \ + bus_space_read_1((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, (off)) + +#define malo_ctl_write4(sc, off, x) \ + bus_space_write_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off), (x)) +#define malo_ctl_read4(sc, off) \ + bus_space_read_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off)) +#define malo_ctl_read1(sc, off) \ + bus_space_read_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off)) + +struct cfdriver malo_cd = { + NULL, "malo", DV_IFNET +}; + +int malo_dma_alloc_cmd(struct malo_softc *sc); +int malo_load_bootimg(struct malo_softc *sc); +int malo_load_firmware(struct malo_softc *sc); +int malo_send_cmd(struct malo_softc *sc, bus_addr_t addr, uint32_t waitfor); +int malo_reset(struct malo_softc *sc); +int malo_get_spec(struct malo_softc *sc); +int malo_init(struct ifnet *ifp); +int malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +void malo_start(struct ifnet *ifp); +int malo_stop(struct malo_softc *sc); +void malo_watchdog(struct ifnet *ifp); +int malo_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, + int arg); +void malo_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, + int isnew); +struct ieee80211_node * + malo_node_alloc(struct ieee80211com *ic); + +/* supported rates */ +const struct ieee80211_rateset malo_rates_11b = + { 4, { 2, 4, 11, 22 } }; +const struct ieee80211_rateset malo_rates_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + +int +malo_intr(void *arg) +{ + struct malo_softc *sc = arg; + uint32_t status; + + status = malo_ctl_read4(sc, 0x0c30); + if (status == 0xffffffff || status == 0) + /* not for us */ + return (0); + + DPRINTF(("%s: INTERRUPT %08x\n", sc->sc_dev.dv_xname, status)); + + if (status & 0x4) { +#ifdef MALO_DEBUG + struct malo_cmdheader *hdr = sc->sc_cmd_mem; + int i; + + printf("%s: command answer", sc->sc_dev.dv_xname); + for (i = 0; i < hdr->size; i++) { + if (i % 16 == 0) + printf("\n%4i:", i); + if (i % 4 == 0) + printf(" "); + printf("%02x", (int)*((u_char *)hdr + i)); + } + printf("\n"); +#endif + /* wakeup caller */ + wakeup(sc); + } + + /* just ack the interrupt */ + malo_ctl_write4(sc, 0x0c30, 0); + return (1); +} + +int +malo_attach(struct malo_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int i; +#if 0 + /* ??? */ + //malo_ctl_write4(sc, 0x0c38, 0x1f); + /* disable interrupts */ + malo_ctl_read4(sc, 0x0c30); + malo_ctl_write4(sc, 0x0c30, 0); + malo_ctl_write4(sc, 0x0c34, 0); + malo_ctl_write4(sc, 0x0c3c, 0); +#endif + /* setup interface */ + ifp->if_softc = sc; + ifp->if_init = malo_init; + ifp->if_ioctl = malo_ioctl; + ifp->if_start = malo_start; + ifp->if_watchdog = malo_watchdog; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + IFQ_SET_READY(&ifp->if_snd); + + /* set supported rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = malo_rates_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = malo_rates_11g; + + /* set channels */ + for (i = 1; i <= 14; i++) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_channels[i].ic_flags = IEEE80211_CHAN_PUREG; + } + + /* set the rest */ + ic->ic_caps = IEEE80211_C_IBSS; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_state = IEEE80211_S_INIT; + for (i = 0; i < 6; i++) + ic->ic_myaddr[i] = malo_ctl_read1(sc, 0xa528 + i); + + /* show our mac address */ + printf(", address: %s\n", ether_sprintf(ic->ic_myaddr)); + + /* attach interface */ + if_attach(ifp); + ieee80211_ifattach(ifp); + + /* post attach vector functions */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = malo_newstate; + ic->ic_newassoc = malo_newassoc; + ic->ic_node_alloc = malo_node_alloc; + + ieee80211_media_init(ifp, ieee80211_media_change, + ieee80211_media_status); + +#if NBPFILTER > 0 + /* TODO bpf mtap */ +#endif + + return (0); +} + +int +malo_detach(void *arg) +{ + struct malo_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + + malo_stop(sc); + ieee80211_ifdetach(ifp); + if_detach(ifp); + //malo_dma_free(sc); + + return (0); +} + +int +malo_dma_alloc_cmd(struct malo_softc *sc) +{ + int error, nsegs; + + error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, + PAGE_SIZE, 0, BUS_DMA_ALLOCNOW, &sc->sc_cmd_dmam); + if (error != 0) { + printf("%s: can not create DMA tag\n", sc->sc_dev.dv_xname); + return (-1); + } + + error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, + 0, &sc->sc_cmd_dmas, 1, &nsegs, BUS_DMA_WAITOK); + if (error != 0) { + printf("%s: error alloc dma memory\n", sc->sc_dev.dv_xname); + return (-1); + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->sc_cmd_dmas, nsegs, + PAGE_SIZE, (caddr_t *)&sc->sc_cmd_mem, BUS_DMA_WAITOK); + if (error != 0) { + printf("%s: error map dma memory\n", sc->sc_dev.dv_xname); + return (-1); + } + + error = bus_dmamap_load(sc->sc_dmat, sc->sc_cmd_dmam, + sc->sc_cmd_mem, PAGE_SIZE, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: error load dma memory\n", sc->sc_dev.dv_xname); + bus_dmamem_free(sc->sc_dmat, &sc->sc_cmd_dmas, nsegs); + return (-1); + } + + sc->sc_cookie = sc->sc_cmd_mem; + *sc->sc_cookie = htole32(0xaa55aa55); + sc->sc_cmd_mem = sc->sc_cmd_mem + sizeof(uint32_t); + sc->sc_cookie_dmaaddr = sc->sc_cmd_dmam->dm_segs[0].ds_addr; + sc->sc_cmd_dmaaddr = sc->sc_cmd_dmam->dm_segs[0].ds_addr + + sizeof(uint32_t); + + return (0); +} + +int +malo_load_bootimg(struct malo_softc *sc) +{ + char *name = "mrv8k-b.fw"; + uint8_t *ucode; + size_t size, count; + int error; + + /* load boot firmware */ + if ((error = loadfirmware(name, &ucode, &size)) != 0) { + DPRINTF(("%s: error %d, could not read microcode %s!\n", + sc->sc_dev.dv_xname, error, name)); + return (EIO); + } + + /* + * It seems we are putting this code directly onto the stack of + * the ARM cpu. I don't know why we need to instruct the DMA + * engine to move the code. This is a big riddle without docu. + */ + DPRINTF(("%s: loading boot firmware\n", sc->sc_dev.dv_xname)); + malo_mem_write2(sc, 0xbef8, 0x001); + malo_mem_write2(sc, 0xbefa, size); + malo_mem_write4(sc, 0xbefc, 0); + + for (count = 0; count < size; count++) + malo_mem_write1(sc, 0xbf00 + count, ucode[count]); + + /* + * we loaded the firmware into card memory now tell the CPU + * to fetch the code and execute it. The memory mapped via the + * first bar is internaly mapped to 0xc0000000. + */ + if (malo_send_cmd(sc, 0xc000bef8, 5) != 0) { + printf("%s: BUMMER: timeout\n", sc->sc_dev.dv_xname); + free(ucode, M_DEVBUF); + return (ETIMEDOUT); + } + free(ucode, M_DEVBUF); + + /* tell the card we're done and... */ + malo_mem_write2(sc, 0xbef8, 0x001); + malo_mem_write2(sc, 0xbefa, 0); + malo_mem_write4(sc, 0xbefc, 0); + malo_send_cmd(sc, 0xc000bef8, 0); + + /* give card a bit time to init */ + delay(50); + DPRINTF(("%s: boot firmware loaded\n", sc->sc_dev.dv_xname)); + + return (0); +} + +int +malo_load_firmware(struct malo_softc *sc) +{ + struct malo_cmdheader *hdr; + char *name = "mrv8k-f.fw"; + void *data; + uint8_t *ucode; + size_t size, count, bsize; + int sn, error; + + /* load real firmware now */ + if ((error = loadfirmware(name, &ucode, &size)) != 0) { + DPRINTF(("%s: error %d, could not read microcode %s!\n", + sc->sc_dev.dv_xname, error, name)); + return (EIO); + } + + DPRINTF(("%s: loading firmware\n", sc->sc_dev.dv_xname)); + hdr = sc->sc_cmd_mem; + data = hdr + 1; + sn = 1; + for (count = 0; count < size; count += bsize) { + bsize = MIN(256, size - count); + + hdr->cmd = htole16(0x0001); + hdr->size = htole16(bsize); + hdr->seqnum = htole16(sn++); + hdr->result = 0; + + bcopy(ucode + count, data, bsize); + + if (malo_send_cmd(sc, sc->sc_cmd_dmaaddr, 5) != 0) { + printf("%s: GRUMBLE: timeout\n", sc->sc_dev.dv_xname); + free(ucode, M_DEVBUF); + return (ETIMEDOUT); + } + + delay(100); + } + free(ucode, M_DEVBUF); + + DPRINTF(("%s: firmware upload finished\n", sc->sc_dev.dv_xname)); + + hdr->cmd = htole16(0x0001); + hdr->size = 0; + hdr->seqnum = htole16(sn++); + hdr->result = 0; + + if (malo_send_cmd(sc, sc->sc_cmd_dmaaddr, 0xf0f1f2f4) != 0) { + printf("%s: GOPF: timeout\n", sc->sc_dev.dv_xname); + return (ETIMEDOUT); + } + + /* give card a bit time to load firmware */ + delay(20000); + DPRINTF(("%s: firmware loaded\n", sc->sc_dev.dv_xname)); + + return (0); +} + +int +malo_send_cmd(struct malo_softc *sc, bus_addr_t addr, uint32_t waitfor) +{ + int i; + + malo_ctl_write4(sc, 0x0c10, (uint32_t)addr); + malo_ctl_read4(sc, 0x0c14); + malo_ctl_write4(sc, 0x0c18, 2); /* CPU_TRANSFER_CMD */ + malo_ctl_read4(sc, 0x0c14); + + if (waitfor == 0) + return (0); + + /* wait for the DMA engine to finish the transfer */ + for (i = 0; i < 100; i++) { + delay(50); + if (malo_ctl_read4(sc, 0x0c14) == waitfor); + break; + } + + if (i == 100) + return (ETIMEDOUT); + + return (0); +} + +int +malo_reset(struct malo_softc *sc) +{ + struct malo_cmdheader *hdr = sc->sc_cmd_mem; + + hdr->cmd = htole16(5); + hdr->size = htole16(sizeof(*hdr)); + hdr->seqnum = 1; + hdr->result = 0; + + malo_send_cmd(sc, sc->sc_cmd_dmaaddr, 0); + + tsleep(sc, 0, "malorst", hz); + + if (hdr->cmd & MALO_CMD_RESPONSE) + return (0); + else + return (ETIMEDOUT); +} + +int +malo_get_spec(struct malo_softc *sc) +{ + struct malo_cmdheader *hdr = sc->sc_cmd_mem; + struct malo_hw_spec *spec; + + hdr->cmd = htole16(MALO_CMD_GET_HW_SPEC); + hdr->size = htole16(sizeof(*hdr) + sizeof(*spec)); + hdr->seqnum = htole16(42); /* the one and only */ + hdr->result = 0; + spec = (struct malo_hw_spec *)(hdr + 1); + + DPRINTF(("%s: fw cmd %04x size %d\n", sc->sc_dev.dv_xname, + hdr->cmd, hdr->size)); + + bzero(spec, sizeof(*spec)); + memset(spec->PermanentAddress, 0xff, ETHER_ADDR_LEN); + spec->CookiePtr = htole32(sc->sc_cookie_dmaaddr); + + malo_send_cmd(sc, sc->sc_cmd_dmaaddr, 0); + + tsleep(sc, 0, "malospc", hz); + + if ((hdr->cmd & MALO_CMD_RESPONSE) == 0) + return (ETIMEDOUT); + + /* XXX get the data form the buffer and feed it to ieee80211 */ + return (0); +} + +int +malo_init(struct ifnet *ifp) +{ + struct malo_softc *sc = ifp->if_softc; + int error; + + DPRINTF(("%s: malo_init\n", ifp->if_xname)); + if (sc->sc_enable) + sc->sc_enable(sc); + + /* ???, what is this for, seems unnecessary */ + /* malo_ctl_write4(sc, 0x0c38, 0x1f); */ + /* disable interrupts */ + malo_ctl_read4(sc, 0x0c30); + malo_ctl_write4(sc, 0x0c30, 0); + malo_ctl_write4(sc, 0x0c34, 0); + malo_ctl_write4(sc, 0x0c3c, 0); + + /* load firmware */ + malo_dma_alloc_cmd(sc); + malo_load_bootimg(sc); + malo_load_firmware(sc); + + /* enable interrupts */ + malo_ctl_write4(sc, 0x0c34, 0x1f); + malo_ctl_read4(sc, 0x0c14); + malo_ctl_write4(sc, 0x0c3c, 0x1f); + malo_ctl_read4(sc, 0x0c14); + + if ((error = malo_get_spec(sc))) + return (error); + + ifp->if_flags |= IFF_RUNNING; + + return (0); +} + +int +malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct malo_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifaddr *ifa; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); +#endif + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_flags & IFF_RUNNING) == 0) + malo_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + malo_stop(sc); + } + break; + default: + error = ieee80211_ioctl(ifp, cmd, data); + break; + } + + splx(s); + + return (error); +} + +void +malo_start(struct ifnet *ifp) +{ + +} + +int +malo_stop(struct malo_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + + /* try to reset card, if the firmware is loaded */ + if (ifp->if_flags & IFF_RUNNING) + malo_reset(sc); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + DPRINTF(("%s: malo_stop\n", ifp->if_xname)); + if (sc->sc_disable) + sc->sc_disable(sc); + + return (0); +} + +void +malo_watchdog(struct ifnet *ifp) +{ + +} + +int +malo_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + return (0); +} + +void +malo_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) +{ + +} + +struct ieee80211_node * +malo_node_alloc(struct ieee80211com *ic) +{ + struct malo_node *wn; + + wn = malloc(sizeof(struct malo_node), M_DEVBUF, M_NOWAIT); + if (wn == NULL) + return (NULL); + + bzero(wn, sizeof(struct malo_node)); + + return ((struct ieee80211_node *)wn); +} diff --git a/sys/dev/ic/malo.h b/sys/dev/ic/malo.h new file mode 100644 index 00000000000..c25cccb4e09 --- /dev/null +++ b/sys/dev/ic/malo.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct malo_softc { + struct device sc_dev; + struct ieee80211com sc_ic; + + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_mem1_bt; + bus_space_tag_t sc_mem2_bt; + bus_space_handle_t sc_mem1_bh; + bus_space_handle_t sc_mem2_bh; + + bus_dmamap_t sc_cmd_dmam; + bus_dma_segment_t sc_cmd_dmas; + void *sc_cmd_mem; + bus_addr_t sc_cmd_dmaaddr; + uint32_t *sc_cookie; + bus_addr_t sc_cookie_dmaaddr; + + int (*sc_newstate) + (struct ieee80211com *, + enum ieee80211_state, int); + + int (*sc_enable)(struct malo_softc *); + void (*sc_disable)(struct malo_softc *); +}; + +struct malo_node { + struct ieee80211_node ni; +}; + +int malo_intr(void *arg); +int malo_attach(struct malo_softc *sc); +int malo_detach(void *arg); |