2011-08-01 15:20:26 +04:00
|
|
|
/* $NetBSD: pccbb.c,v 1.203 2011/08/01 11:20:26 drochner Exp $ */
|
1999-10-15 10:42:21 +04:00
|
|
|
|
1999-10-15 10:07:17 +04:00
|
|
|
/*
|
2000-01-26 12:02:41 +03:00
|
|
|
* Copyright (c) 1998, 1999 and 2000
|
|
|
|
* HAYAKAWA Koichi. All rights reserved.
|
1999-10-15 10:07:17 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:48:40 +03:00
|
|
|
#include <sys/cdefs.h>
|
2011-08-01 15:20:26 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.203 2011/08/01 11:20:26 drochner Exp $");
|
2001-11-13 10:48:40 +03:00
|
|
|
|
1999-10-15 10:07:17 +04:00
|
|
|
/*
|
|
|
|
#define CBB_DEBUG
|
|
|
|
#define SHOW_REGS
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/ioctl.h>
|
2001-01-22 04:13:47 +03:00
|
|
|
#include <sys/reboot.h> /* for bootverbose */
|
1999-10-15 10:07:17 +04:00
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/malloc.h>
|
2001-01-24 13:10:04 +03:00
|
|
|
#include <sys/proc.h>
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/intr.h>
|
|
|
|
#include <sys/bus.h>
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
|
|
|
|
#include <dev/pci/pccbbreg.h>
|
|
|
|
|
|
|
|
#include <dev/cardbus/cardslotvar.h>
|
|
|
|
|
|
|
|
#include <dev/cardbus/cardbusvar.h>
|
|
|
|
|
|
|
|
#include <dev/pcmcia/pcmciareg.h>
|
|
|
|
#include <dev/pcmcia/pcmciavar.h>
|
|
|
|
|
|
|
|
#include <dev/ic/i82365reg.h>
|
|
|
|
#include <dev/pci/pccbbvar.h>
|
|
|
|
|
|
|
|
#ifndef __NetBSD_Version__
|
|
|
|
struct cfdriver cbb_cd = {
|
2000-02-05 21:20:08 +03:00
|
|
|
NULL, "cbb", DV_DULL
|
1999-10-15 10:07:17 +04:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2001-12-24 12:41:34 +03:00
|
|
|
#ifdef CBB_DEBUG
|
1999-10-15 10:07:17 +04:00
|
|
|
#define DPRINTF(x) printf x
|
|
|
|
#define STATIC
|
|
|
|
#else
|
|
|
|
#define DPRINTF(x)
|
|
|
|
#define STATIC static
|
|
|
|
#endif
|
|
|
|
|
2007-11-16 21:36:51 +03:00
|
|
|
int pccbb_burstup = 1;
|
|
|
|
|
2001-01-24 13:10:04 +03:00
|
|
|
/*
|
2007-02-04 08:08:18 +03:00
|
|
|
* delay_ms() is wait in milliseconds. It should be used instead
|
2007-01-26 05:27:29 +03:00
|
|
|
* of delay() if you want to wait more than 1 ms.
|
2001-01-24 13:10:04 +03:00
|
|
|
*/
|
2007-02-04 08:08:18 +03:00
|
|
|
static inline void
|
2009-07-24 01:22:25 +04:00
|
|
|
delay_ms(int millis, struct pccbb_softc *sc)
|
2007-02-04 08:08:18 +03:00
|
|
|
{
|
|
|
|
if (cold)
|
|
|
|
delay(millis * 1000);
|
|
|
|
else
|
2009-07-24 01:22:25 +04:00
|
|
|
kpause("pccbb", false, mstohz(millis), NULL);
|
2007-02-04 08:08:18 +03:00
|
|
|
}
|
2001-01-24 13:10:04 +03:00
|
|
|
|
2009-05-06 13:25:14 +04:00
|
|
|
int pcicbbmatch(device_t, cfdata_t, void *);
|
2008-01-14 09:12:13 +03:00
|
|
|
void pccbbattach(device_t, device_t, void *);
|
2009-05-21 21:32:32 +04:00
|
|
|
void pccbbchilddet(device_t, device_t);
|
2007-12-17 00:28:30 +03:00
|
|
|
int pccbbdetach(device_t, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
int pccbbintr(void *);
|
|
|
|
static void pci113x_insert(void *);
|
|
|
|
static int pccbbintr_function(struct pccbb_softc *);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
static int pccbb_detect_card(struct pccbb_softc *);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-25 19:29:23 +04:00
|
|
|
static void pccbb_pcmcia_write(struct pccbb_softc *, int, u_int8_t);
|
|
|
|
static u_int8_t pccbb_pcmcia_read(struct pccbb_softc *, int);
|
2008-06-27 00:57:10 +04:00
|
|
|
#define Pcic_read(sc, reg) pccbb_pcmcia_read((sc), (reg))
|
|
|
|
#define Pcic_write(sc, reg, val) pccbb_pcmcia_write((sc), (reg), (val))
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
STATIC int cb_reset(struct pccbb_softc *);
|
|
|
|
STATIC int cb_detect_voltage(struct pccbb_softc *);
|
|
|
|
STATIC int cbbprint(void *, const char *);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
static int cb_chipset(u_int32_t, int *);
|
|
|
|
STATIC void pccbb_pcmcia_attach_setup(struct pccbb_softc *,
|
|
|
|
struct pcmciabus_attach_args *);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
STATIC int pccbb_ctrl(cardbus_chipset_tag_t, int);
|
2008-01-03 02:11:34 +03:00
|
|
|
STATIC int pccbb_power(struct pccbb_softc *sc, int);
|
|
|
|
STATIC int pccbb_power_ct(cardbus_chipset_tag_t, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
STATIC int pccbb_cardenable(struct pccbb_softc * sc, int function);
|
2008-06-24 23:44:51 +04:00
|
|
|
static void *pccbb_intr_establish(struct pccbb_softc *,
|
2011-08-01 15:20:26 +04:00
|
|
|
int level, int (*ih) (void *), void *sc);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void pccbb_intr_disestablish(struct pccbb_softc *, void *ih);
|
|
|
|
|
2008-06-24 23:44:51 +04:00
|
|
|
static void *pccbb_cb_intr_establish(cardbus_chipset_tag_t,
|
2011-08-01 15:20:26 +04:00
|
|
|
int level, int (*ih) (void *), void *sc);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void pccbb_cb_intr_disestablish(cardbus_chipset_tag_t ct, void *ih);
|
|
|
|
|
2010-02-25 02:38:40 +03:00
|
|
|
static pcitag_t pccbb_make_tag(cardbus_chipset_tag_t, int, int);
|
|
|
|
static pcireg_t pccbb_conf_read(cardbus_chipset_tag_t, pcitag_t, int);
|
|
|
|
static void pccbb_conf_write(cardbus_chipset_tag_t, pcitag_t, int,
|
|
|
|
pcireg_t);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void pccbb_chipinit(struct pccbb_softc *);
|
2008-02-02 03:31:25 +03:00
|
|
|
static void pccbb_intrinit(struct pccbb_softc *);
|
2005-02-04 05:10:35 +03:00
|
|
|
|
|
|
|
STATIC int pccbb_pcmcia_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
|
|
|
|
struct pcmcia_mem_handle *);
|
|
|
|
STATIC void pccbb_pcmcia_mem_free(pcmcia_chipset_handle_t,
|
|
|
|
struct pcmcia_mem_handle *);
|
|
|
|
STATIC int pccbb_pcmcia_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t,
|
2009-02-14 01:39:37 +03:00
|
|
|
bus_size_t, struct pcmcia_mem_handle *, bus_size_t *, int *);
|
2005-02-04 05:10:35 +03:00
|
|
|
STATIC void pccbb_pcmcia_mem_unmap(pcmcia_chipset_handle_t, int);
|
|
|
|
STATIC int pccbb_pcmcia_io_alloc(pcmcia_chipset_handle_t, bus_addr_t,
|
|
|
|
bus_size_t, bus_size_t, struct pcmcia_io_handle *);
|
|
|
|
STATIC void pccbb_pcmcia_io_free(pcmcia_chipset_handle_t,
|
|
|
|
struct pcmcia_io_handle *);
|
|
|
|
STATIC int pccbb_pcmcia_io_map(pcmcia_chipset_handle_t, int, bus_addr_t,
|
|
|
|
bus_size_t, struct pcmcia_io_handle *, int *);
|
|
|
|
STATIC void pccbb_pcmcia_io_unmap(pcmcia_chipset_handle_t, int);
|
|
|
|
STATIC void *pccbb_pcmcia_intr_establish(pcmcia_chipset_handle_t,
|
|
|
|
struct pcmcia_function *, int, int (*)(void *), void *);
|
|
|
|
STATIC void pccbb_pcmcia_intr_disestablish(pcmcia_chipset_handle_t, void *);
|
|
|
|
STATIC void pccbb_pcmcia_socket_enable(pcmcia_chipset_handle_t);
|
|
|
|
STATIC void pccbb_pcmcia_socket_disable(pcmcia_chipset_handle_t);
|
|
|
|
STATIC void pccbb_pcmcia_socket_settype(pcmcia_chipset_handle_t, int);
|
|
|
|
STATIC int pccbb_pcmcia_card_detect(pcmcia_chipset_handle_t pch);
|
|
|
|
|
2008-06-26 22:05:48 +04:00
|
|
|
static int pccbb_pcmcia_wait_ready(struct pccbb_softc *);
|
|
|
|
static void pccbb_pcmcia_delay(struct pccbb_softc *, int, const char *);
|
2005-02-04 05:10:35 +03:00
|
|
|
|
2008-06-26 22:05:48 +04:00
|
|
|
static void pccbb_pcmcia_do_io_map(struct pccbb_softc *, int);
|
|
|
|
static void pccbb_pcmcia_do_mem_map(struct pccbb_softc *, int);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-14 04:29:30 +03:00
|
|
|
/* bus-space allocation and deallocation functions */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
static int pccbb_rbus_cb_space_alloc(cardbus_chipset_tag_t, rbus_tag_t,
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t addr, bus_size_t size, bus_addr_t mask, bus_size_t align,
|
2005-02-04 05:10:35 +03:00
|
|
|
int flags, bus_addr_t * addrp, bus_space_handle_t * bshp);
|
|
|
|
static int pccbb_rbus_cb_space_free(cardbus_chipset_tag_t, rbus_tag_t,
|
|
|
|
bus_space_handle_t, bus_size_t);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
static int pccbb_open_win(struct pccbb_softc *, bus_space_tag_t,
|
|
|
|
bus_addr_t, bus_size_t, bus_space_handle_t, int flags);
|
|
|
|
static int pccbb_close_win(struct pccbb_softc *, bus_space_tag_t,
|
|
|
|
bus_space_handle_t, bus_size_t);
|
|
|
|
static int pccbb_winlist_insert(struct pccbb_win_chain_head *, bus_addr_t,
|
|
|
|
bus_size_t, bus_space_handle_t, int);
|
|
|
|
static int pccbb_winlist_delete(struct pccbb_win_chain_head *,
|
|
|
|
bus_space_handle_t, bus_size_t);
|
|
|
|
static void pccbb_winset(bus_addr_t align, struct pccbb_softc *,
|
|
|
|
bus_space_tag_t);
|
1999-10-15 10:07:17 +04:00
|
|
|
void pccbb_winlist_show(struct pccbb_win_chain *);
|
|
|
|
|
|
|
|
|
|
|
|
/* for config_defer */
|
2008-01-14 09:12:13 +03:00
|
|
|
static void pccbb_pci_callback(device_t);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-02-25 01:37:54 +03:00
|
|
|
static bool pccbb_suspend(device_t, const pmf_qual_t *);
|
|
|
|
static bool pccbb_resume(device_t, const pmf_qual_t *);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined SHOW_REGS
|
2005-02-04 05:10:35 +03:00
|
|
|
static void cb_show_regs(pci_chipset_tag_t pc, pcitag_t tag,
|
|
|
|
bus_space_tag_t memt, bus_space_handle_t memh);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
CFATTACH_DECL3_NEW(cbb_pci, sizeof(struct pccbb_softc),
|
2009-05-21 21:32:32 +04:00
|
|
|
pcicbbmatch, pccbbattach, pccbbdetach, NULL, NULL, pccbbchilddet,
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
DVF_DETACH_SHUTDOWN);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-26 16:33:17 +04:00
|
|
|
static const struct pcmcia_chip_functions pccbb_pcmcia_funcs = {
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_pcmcia_mem_alloc,
|
|
|
|
pccbb_pcmcia_mem_free,
|
|
|
|
pccbb_pcmcia_mem_map,
|
|
|
|
pccbb_pcmcia_mem_unmap,
|
|
|
|
pccbb_pcmcia_io_alloc,
|
|
|
|
pccbb_pcmcia_io_free,
|
|
|
|
pccbb_pcmcia_io_map,
|
|
|
|
pccbb_pcmcia_io_unmap,
|
|
|
|
pccbb_pcmcia_intr_establish,
|
|
|
|
pccbb_pcmcia_intr_disestablish,
|
|
|
|
pccbb_pcmcia_socket_enable,
|
|
|
|
pccbb_pcmcia_socket_disable,
|
2004-08-11 04:18:18 +04:00
|
|
|
pccbb_pcmcia_socket_settype,
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_pcmcia_card_detect
|
1999-10-15 10:07:17 +04:00
|
|
|
};
|
|
|
|
|
2008-06-26 16:33:17 +04:00
|
|
|
static const struct cardbus_functions pccbb_funcs = {
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_rbus_cb_space_alloc,
|
|
|
|
pccbb_rbus_cb_space_free,
|
2000-02-23 10:28:54 +03:00
|
|
|
pccbb_cb_intr_establish,
|
|
|
|
pccbb_cb_intr_disestablish,
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_ctrl,
|
2008-01-03 02:11:34 +03:00
|
|
|
pccbb_power_ct,
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_make_tag,
|
|
|
|
pccbb_conf_read,
|
|
|
|
pccbb_conf_write,
|
1999-10-15 10:07:17 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
int
|
2009-05-06 13:25:14 +04:00
|
|
|
pcicbbmatch(device_t parent, cfdata_t match, void *aux)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
|
|
|
|
PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_CARDBUS &&
|
|
|
|
PCI_INTERFACE(pa->pa_class) == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define MAKEID(vendor, prod) (((vendor) << PCI_VENDOR_SHIFT) \
|
|
|
|
| ((prod) << PCI_PRODUCT_SHIFT))
|
|
|
|
|
2001-02-22 00:39:52 +03:00
|
|
|
const struct yenta_chipinfo {
|
2000-02-05 21:20:08 +03:00
|
|
|
pcireg_t yc_id; /* vendor tag | product tag */
|
|
|
|
int yc_chiptype;
|
|
|
|
int yc_flags;
|
1999-10-15 10:07:17 +04:00
|
|
|
} yc_chipsets[] = {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* Texas Instruments chips */
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1130), CB_TI113X,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1131), CB_TI113X,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2004-03-28 13:49:31 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1250), CB_TI125X,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1220), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1221), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1225), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2004-03-28 13:49:31 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1251), CB_TI125X,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2004-03-28 13:49:31 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1251B), CB_TI125X,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1211), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2001-05-19 23:46:08 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1410), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2007-11-16 21:36:51 +03:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1420), CB_TI1420,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2004-03-28 13:49:31 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1450), CB_TI125X,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1451), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2010-12-27 22:02:32 +03:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1510), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2004-07-28 19:32:49 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1520), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2002-11-09 11:51:14 +03:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI4410YENTA), CB_TI12XX,
|
2004-07-28 19:32:49 +04:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI4520YENTA), CB_TI12XX,
|
2002-11-09 11:51:14 +03:00
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2008-10-25 22:46:38 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI7420YENTA), CB_TI12XX,
|
|
|
|
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
|
2000-02-05 21:20:08 +03:00
|
|
|
|
|
|
|
/* Ricoh chips */
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_Rx5C475), CB_RX5C47X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_RL5C476), CB_RX5C47X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_Rx5C477), CB_RX5C47X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_Rx5C478), CB_RX5C47X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_Rx5C465), CB_RX5C46X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_RICOH, PCI_PRODUCT_RICOH_Rx5C466), CB_RX5C46X,
|
|
|
|
PCCBB_PCMCIA_MEM_32},
|
|
|
|
|
|
|
|
/* Toshiba products */
|
|
|
|
{ MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC95),
|
|
|
|
CB_TOPIC95, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC95B),
|
|
|
|
CB_TOPIC95B, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC97),
|
|
|
|
CB_TOPIC97, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_TOSHIBA2, PCI_PRODUCT_TOSHIBA2_ToPIC100),
|
|
|
|
CB_TOPIC97, PCCBB_PCMCIA_MEM_32},
|
|
|
|
|
|
|
|
/* Cirrus Logic products */
|
|
|
|
{ MAKEID(PCI_VENDOR_CIRRUS, PCI_PRODUCT_CIRRUS_CL_PD6832),
|
|
|
|
CB_CIRRUS, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_CIRRUS, PCI_PRODUCT_CIRRUS_CL_PD6833),
|
|
|
|
CB_CIRRUS, PCCBB_PCMCIA_MEM_32},
|
|
|
|
|
2008-05-28 01:32:47 +04:00
|
|
|
/* O2 Micro products */
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6729),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6730),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6832),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6836),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6872),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6922),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6933),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_OZ6972),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
2008-08-06 19:50:46 +04:00
|
|
|
{ MAKEID(PCI_VENDOR_O2MICRO, PCI_PRODUCT_O2MICRO_7223),
|
|
|
|
CB_O2MICRO, PCCBB_PCMCIA_MEM_32},
|
2008-05-28 01:32:47 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* sentinel, or Generic chip */
|
|
|
|
{ 0 /* null id */ , CB_UNKNOWN, PCCBB_PCMCIA_MEM_32},
|
1999-10-15 10:07:17 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
cb_chipset(u_int32_t pci_id, int *flagp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2001-02-22 00:39:52 +03:00
|
|
|
const struct yenta_chipinfo *yc;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-14 13:26:10 +03:00
|
|
|
/* Loop over except the last default entry. */
|
|
|
|
for (yc = yc_chipsets; yc < yc_chipsets +
|
2008-05-22 05:22:17 +04:00
|
|
|
__arraycount(yc_chipsets) - 1; yc++)
|
2000-05-08 11:31:20 +04:00
|
|
|
if (pci_id == yc->yc_id)
|
2000-03-14 13:26:10 +03:00
|
|
|
break;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-14 13:26:10 +03:00
|
|
|
if (flagp != NULL)
|
|
|
|
*flagp = yc->yc_flags;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-14 13:26:10 +03:00
|
|
|
return (yc->yc_chiptype);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2009-05-21 21:32:32 +04:00
|
|
|
void
|
|
|
|
pccbbchilddet(device_t self, device_t child)
|
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = device_private(self);
|
|
|
|
int s;
|
|
|
|
|
|
|
|
KASSERT(sc->sc_csc == device_private(child));
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (sc->sc_csc == device_private(child))
|
|
|
|
sc->sc_csc = NULL;
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
1999-10-15 10:07:17 +04:00
|
|
|
void
|
2008-01-14 09:12:13 +03:00
|
|
|
pccbbattach(device_t parent, device_t self, void *aux)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-01-14 09:12:13 +03:00
|
|
|
struct pccbb_softc *sc = device_private(self);
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
2000-07-10 01:58:30 +04:00
|
|
|
pcireg_t busreg, reg, sock_base;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t sockbase;
|
|
|
|
char devinfo[256];
|
|
|
|
int flags;
|
|
|
|
|
2003-03-22 09:25:14 +03:00
|
|
|
#ifdef __HAVE_PCCBB_ATTACH_HOOK
|
|
|
|
pccbb_attach_hook(parent, self, pa);
|
|
|
|
#endif
|
|
|
|
|
2008-06-25 15:42:32 +04:00
|
|
|
sc->sc_dev = self;
|
|
|
|
|
2009-07-24 01:22:25 +04:00
|
|
|
mutex_init(&sc->sc_pwr_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
|
|
cv_init(&sc->sc_pwr_cv, "pccpwr");
|
|
|
|
|
2007-10-22 18:03:51 +04:00
|
|
|
callout_init(&sc->sc_insert_ch, 0);
|
|
|
|
callout_setfunc(&sc->sc_insert_ch, pci113x_insert, sc);
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_chipset = cb_chipset(pa->pa_id, &flags);
|
|
|
|
|
2007-12-01 07:50:50 +03:00
|
|
|
aprint_naive("\n");
|
|
|
|
|
2004-04-24 01:13:05 +04:00
|
|
|
pci_devinfo(pa->pa_id, 0, 0, devinfo, sizeof(devinfo));
|
2007-12-01 07:50:50 +03:00
|
|
|
aprint_normal(": %s (rev. 0x%02x)", devinfo,
|
|
|
|
PCI_REVISION(pa->pa_class));
|
2006-07-09 03:02:55 +04:00
|
|
|
DPRINTF((" (chipflags %x)", flags));
|
2007-12-01 07:50:50 +03:00
|
|
|
aprint_normal("\n");
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
TAILQ_INIT(&sc->sc_memwindow);
|
|
|
|
TAILQ_INIT(&sc->sc_iowindow);
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_rbus_iot = rbus_pccbb_parent_io(pa);
|
|
|
|
sc->sc_rbus_memt = rbus_pccbb_parent_mem(pa);
|
2001-07-06 22:06:59 +04:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("pa->pa_memt: %08x vs rbus_mem->rb_bt: %08x\n",
|
|
|
|
pa->pa_memt, sc->sc_rbus_memt->rb_bt);
|
|
|
|
#endif
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2003-03-22 09:25:14 +03:00
|
|
|
sc->sc_flags &= ~CBB_MEMHMAPPED;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* MAP socket registers and ExCA registers on memory-space
|
|
|
|
* When no valid address is set on socket base registers (on pci
|
|
|
|
* config space), get it not polite way.
|
|
|
|
*/
|
|
|
|
sock_base = pci_conf_read(pc, pa->pa_tag, PCI_SOCKBASE);
|
|
|
|
|
|
|
|
if (PCI_MAPREG_MEM_ADDR(sock_base) >= 0x100000 &&
|
|
|
|
PCI_MAPREG_MEM_ADDR(sock_base) != 0xfffffff0) {
|
|
|
|
/* The address must be valid. */
|
|
|
|
if (pci_mapreg_map(pa, PCI_SOCKBASE, PCI_MAPREG_TYPE_MEM, 0,
|
2007-12-17 00:28:30 +03:00
|
|
|
&sc->sc_base_memt, &sc->sc_base_memh, &sockbase, &sc->sc_base_size)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(self,
|
2008-02-02 00:13:44 +03:00
|
|
|
"can't map socket base address 0x%lx\n",
|
|
|
|
(unsigned long)sock_base);
|
2000-02-05 21:20:08 +03:00
|
|
|
/*
|
|
|
|
* I think it's funny: socket base registers must be
|
|
|
|
* mapped on memory space, but ...
|
|
|
|
*/
|
|
|
|
if (pci_mapreg_map(pa, PCI_SOCKBASE, PCI_MAPREG_TYPE_IO,
|
|
|
|
0, &sc->sc_base_memt, &sc->sc_base_memh, &sockbase,
|
2007-12-17 00:28:30 +03:00
|
|
|
&sc->sc_base_size)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(self,
|
2008-02-02 00:13:44 +03:00
|
|
|
"can't map socket base address"
|
2009-08-07 16:04:43 +04:00
|
|
|
" 0x%lx: io mode\n",
|
2001-04-30 06:49:04 +04:00
|
|
|
(unsigned long)sockbase);
|
2000-02-05 21:20:08 +03:00
|
|
|
/* give up... allocate reg space via rbus. */
|
|
|
|
pci_conf_write(pc, pa->pa_tag, PCI_SOCKBASE, 0);
|
2003-03-22 09:25:14 +03:00
|
|
|
} else
|
|
|
|
sc->sc_flags |= CBB_MEMHMAPPED;
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
|
|
|
DPRINTF(("%s: socket base address 0x%lx\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(self),
|
2008-02-02 00:13:44 +03:00
|
|
|
(unsigned long)sockbase));
|
2003-03-22 09:25:14 +03:00
|
|
|
sc->sc_flags |= CBB_MEMHMAPPED;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_mem_start = 0; /* XXX */
|
|
|
|
sc->sc_mem_end = 0xffffffff; /* XXX */
|
|
|
|
|
|
|
|
busreg = pci_conf_read(pc, pa->pa_tag, PCI_BUSNUM);
|
1999-10-19 13:29:46 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* pccbb_machdep.c end */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if defined CBB_DEBUG
|
2000-02-05 21:20:08 +03:00
|
|
|
{
|
2005-06-01 13:10:57 +04:00
|
|
|
static const char *intrname[] = { "NON", "A", "B", "C", "D" };
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_debug_dev(self, "intrpin %s, intrtag %d\n",
|
2000-02-06 11:14:13 +03:00
|
|
|
intrname[pa->pa_intrpin], pa->pa_intrline);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* setup softc */
|
|
|
|
sc->sc_pc = pc;
|
|
|
|
sc->sc_iot = pa->pa_iot;
|
|
|
|
sc->sc_memt = pa->pa_memt;
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
sc->sc_tag = pa->pa_tag;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-12-29 01:59:06 +03:00
|
|
|
memcpy(&sc->sc_pa, pa, sizeof(*pa));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_pcmcia_flags = flags; /* set PCMCIA facility */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-07-10 01:58:30 +04:00
|
|
|
/* Disable legacy register mapping. */
|
|
|
|
switch (sc->sc_chipset) {
|
|
|
|
case CB_RX5C46X: /* fallthrough */
|
|
|
|
#if 0
|
2000-07-10 02:06:02 +04:00
|
|
|
/* The RX5C47X-series requires writes to the PCI_LEGACY register. */
|
2000-07-10 01:58:30 +04:00
|
|
|
case CB_RX5C47X:
|
|
|
|
#endif
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-07-10 02:06:02 +04:00
|
|
|
* The legacy pcic io-port on Ricoh RX5C46X CardBus bridges
|
|
|
|
* cannot be disabled by substituting 0 into PCI_LEGACY
|
|
|
|
* register. Ricoh CardBus bridges have special bits on Bridge
|
|
|
|
* control reg (addr 0x3e on PCI config space).
|
2000-07-10 01:58:30 +04:00
|
|
|
*/
|
2007-08-11 04:31:04 +04:00
|
|
|
reg = pci_conf_read(pc, pa->pa_tag, PCI_BRIDGE_CONTROL_REG);
|
2000-07-10 01:58:30 +04:00
|
|
|
reg &= ~(CB_BCRI_RL_3E0_ENA | CB_BCRI_RL_3E2_ENA);
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(pc, pa->pa_tag, PCI_BRIDGE_CONTROL_REG, reg);
|
2000-07-10 01:58:30 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* XXX I don't know proper way to kill legacy I/O. */
|
|
|
|
pci_conf_write(pc, pa->pa_tag, PCI_LEGACY, 0x0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
if (!pmf_device_register(self, pccbb_suspend, pccbb_resume))
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
config_defer(self, pccbb_pci_callback);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
int
|
|
|
|
pccbbdetach(device_t self, int flags)
|
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = device_private(self);
|
|
|
|
pci_chipset_tag_t pc = sc->sc_pa.pa_pc;
|
|
|
|
bus_space_tag_t bmt = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t bmh = sc->sc_base_memh;
|
|
|
|
uint32_t sockmask;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((rc = config_detach_children(self, flags)) != 0)
|
|
|
|
return rc;
|
|
|
|
|
2008-01-14 06:01:41 +03:00
|
|
|
if (!LIST_EMPTY(&sc->sc_pil)) {
|
|
|
|
panic("%s: interrupt handlers still registered",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(self));
|
2008-01-14 06:01:41 +03:00
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
if (sc->sc_ih != NULL) {
|
|
|
|
pci_intr_disestablish(pc, sc->sc_ih);
|
|
|
|
sc->sc_ih = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CSC Interrupt: turn off card detect and power cycle interrupts */
|
|
|
|
sockmask = bus_space_read_4(bmt, bmh, CB_SOCKET_MASK);
|
2008-02-02 03:31:25 +03:00
|
|
|
sockmask &= ~(CB_SOCKET_MASK_CSTS | CB_SOCKET_MASK_CD |
|
|
|
|
CB_SOCKET_MASK_POWER);
|
2007-12-17 00:28:30 +03:00
|
|
|
bus_space_write_4(bmt, bmh, CB_SOCKET_MASK, sockmask);
|
|
|
|
/* reset interrupt */
|
|
|
|
bus_space_write_4(bmt, bmh, CB_SOCKET_EVENT,
|
|
|
|
bus_space_read_4(bmt, bmh, CB_SOCKET_EVENT));
|
|
|
|
|
|
|
|
switch (sc->sc_flags & (CBB_MEMHMAPPED|CBB_SPECMAPPED)) {
|
|
|
|
case CBB_MEMHMAPPED:
|
|
|
|
bus_space_unmap(bmt, bmh, sc->sc_base_size);
|
|
|
|
break;
|
|
|
|
case CBB_MEMHMAPPED|CBB_SPECMAPPED:
|
|
|
|
#if rbus
|
|
|
|
{
|
|
|
|
pcireg_t sockbase;
|
2000-02-23 10:28:54 +03:00
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
sockbase = pci_conf_read(pc, sc->sc_tag, PCI_SOCKBASE);
|
|
|
|
rbus_space_free(sc->sc_rbus_memt, bmh, 0x1000,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
bus_space_free(bmt, bmh, 0x1000);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
sc->sc_flags &= ~(CBB_MEMHMAPPED|CBB_SPECMAPPED);
|
2000-02-23 10:28:54 +03:00
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
if (!TAILQ_EMPTY(&sc->sc_iowindow))
|
|
|
|
aprint_error_dev(self, "i/o windows not empty");
|
|
|
|
if (!TAILQ_EMPTY(&sc->sc_memwindow))
|
|
|
|
aprint_error_dev(self, "memory windows not empty");
|
|
|
|
|
|
|
|
callout_stop(&sc->sc_insert_ch);
|
|
|
|
callout_destroy(&sc->sc_insert_ch);
|
2009-08-07 16:09:50 +04:00
|
|
|
|
|
|
|
mutex_destroy(&sc->sc_pwr_mtx);
|
|
|
|
cv_destroy(&sc->sc_pwr_cv);
|
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2000-02-23 10:28:54 +03:00
|
|
|
|
|
|
|
/*
|
2008-01-14 09:12:13 +03:00
|
|
|
* static void pccbb_pci_callback(device_t self)
|
2000-02-23 10:28:54 +03:00
|
|
|
*
|
|
|
|
* The actual attach routine: get memory space for YENTA register
|
|
|
|
* space, setup YENTA register and route interrupt.
|
|
|
|
*
|
|
|
|
* This function should be deferred because this device may obtain
|
|
|
|
* memory space dynamically. This function must avoid obtaining
|
2000-07-10 01:58:30 +04:00
|
|
|
* memory area which has already kept for another device.
|
2000-02-23 10:28:54 +03:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static void
|
2008-01-14 09:12:13 +03:00
|
|
|
pccbb_pci_callback(device_t self)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-01-14 09:12:13 +03:00
|
|
|
struct pccbb_softc *sc = device_private(self);
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_chipset_tag_t pc = sc->sc_pc;
|
|
|
|
bus_addr_t sockbase;
|
|
|
|
struct cbslot_attach_args cba;
|
|
|
|
struct pcmciabus_attach_args paa;
|
|
|
|
struct cardslot_attach_args caa;
|
2008-06-25 15:42:32 +04:00
|
|
|
device_t csc;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2003-03-22 09:25:14 +03:00
|
|
|
if (!(sc->sc_flags & CBB_MEMHMAPPED)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* The socket registers aren't mapped correctly. */
|
1999-10-15 10:07:17 +04:00
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
if (rbus_space_alloc(sc->sc_rbus_memt, 0, 0x1000, 0x0fff,
|
|
|
|
(sc->sc_chipset == CB_RX5C47X
|
|
|
|
|| sc->sc_chipset == CB_TI113X) ? 0x10000 : 0x1000,
|
|
|
|
0, &sockbase, &sc->sc_base_memh)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sc->sc_base_memt = sc->sc_memt;
|
|
|
|
pci_conf_write(pc, sc->sc_tag, PCI_SOCKBASE, sockbase);
|
2005-06-01 12:39:32 +04:00
|
|
|
DPRINTF(("%s: CardBus register address 0x%lx -> 0x%lx\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(self), (unsigned long)sockbase,
|
2003-12-19 21:16:43 +03:00
|
|
|
(unsigned long)pci_conf_read(pc, sc->sc_tag,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCI_SOCKBASE)));
|
1999-10-15 10:07:17 +04:00
|
|
|
#else
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_base_memt = sc->sc_memt;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if !defined CBB_PCI_BASE
|
|
|
|
#define CBB_PCI_BASE 0x20000000
|
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
if (bus_space_alloc(sc->sc_base_memt, CBB_PCI_BASE, 0xffffffff,
|
|
|
|
0x1000, 0x1000, 0, 0, &sockbase, &sc->sc_base_memh)) {
|
|
|
|
/* cannot allocate memory space */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pci_conf_write(pc, sc->sc_tag, PCI_SOCKBASE, sockbase);
|
2005-06-01 12:39:32 +04:00
|
|
|
DPRINTF(("%s: CardBus register address 0x%lx -> 0x%lx\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(self), (unsigned long)sock_base,
|
2003-12-19 21:16:43 +03:00
|
|
|
(unsigned long)pci_conf_read(pc,
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_tag, PCI_SOCKBASE)));
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2009-05-03 04:31:12 +04:00
|
|
|
sc->sc_flags |= CBB_MEMHMAPPED|CBB_SPECMAPPED;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-04-06 13:11:57 +04:00
|
|
|
/* clear data structure for child device interrupt handlers */
|
2002-10-01 13:09:16 +04:00
|
|
|
LIST_INIT(&sc->sc_pil);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2008-02-02 03:31:25 +03:00
|
|
|
/* bus bridge initialization */
|
|
|
|
pccbb_chipinit(sc);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-04-21 03:39:10 +04:00
|
|
|
sc->sc_pil_intr_enable = true;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
{
|
2001-10-17 14:25:51 +04:00
|
|
|
u_int32_t sockstat;
|
|
|
|
|
|
|
|
sockstat = bus_space_read_4(sc->sc_base_memt,
|
|
|
|
sc->sc_base_memh, CB_SOCKET_STAT);
|
2000-02-05 21:20:08 +03:00
|
|
|
if (0 == (sockstat & CB_SOCKET_STAT_CD)) {
|
|
|
|
sc->sc_flags |= CBB_CARDEXIST;
|
|
|
|
}
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
|
|
|
* attach cardbus
|
2000-02-05 21:20:08 +03:00
|
|
|
*/
|
2004-07-22 20:39:51 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
pcireg_t busreg = pci_conf_read(pc, sc->sc_tag, PCI_BUSNUM);
|
|
|
|
pcireg_t bhlc = pci_conf_read(pc, sc->sc_tag, PCI_BHLC_REG);
|
|
|
|
|
2000-03-14 04:29:30 +03:00
|
|
|
/* initialize cbslot_attach */
|
2000-02-05 21:20:08 +03:00
|
|
|
cba.cba_iot = sc->sc_iot;
|
|
|
|
cba.cba_memt = sc->sc_memt;
|
|
|
|
cba.cba_dmat = sc->sc_dmat;
|
|
|
|
cba.cba_bus = (busreg >> 8) & 0x0ff;
|
|
|
|
cba.cba_cc = (void *)sc;
|
|
|
|
cba.cba_cf = &pccbb_funcs;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
cba.cba_rbus_iot = sc->sc_rbus_iot;
|
|
|
|
cba.cba_rbus_memt = sc->sc_rbus_memt;
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
cba.cba_cacheline = PCI_CACHELINE(bhlc);
|
2007-11-16 21:36:51 +03:00
|
|
|
cba.cba_max_lattimer = PCI_LATTIMER(bhlc);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_verbose_dev(self,
|
2008-02-02 00:13:44 +03:00
|
|
|
"cacheline 0x%x lattimer 0x%x\n",
|
|
|
|
cba.cba_cacheline,
|
|
|
|
cba.cba_max_lattimer);
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_verbose_dev(self, "bhlc 0x%x\n", bhlc);
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined SHOW_REGS
|
2000-02-05 21:20:08 +03:00
|
|
|
cb_show_regs(sc->sc_pc, sc->sc_tag, sc->sc_base_memt,
|
|
|
|
sc->sc_base_memh);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_pcmcia_attach_setup(sc, &paa);
|
|
|
|
caa.caa_cb_attach = NULL;
|
2004-07-22 20:39:51 +04:00
|
|
|
if (cba.cba_bus == 0)
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(self,
|
2008-02-02 00:13:44 +03:00
|
|
|
"secondary bus number uninitialized; try PCI_BUS_FIXUP\n");
|
2004-07-22 20:39:51 +04:00
|
|
|
else
|
2000-02-05 21:20:08 +03:00
|
|
|
caa.caa_cb_attach = &cba;
|
|
|
|
caa.caa_16_attach = &paa;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-02-02 03:31:25 +03:00
|
|
|
pccbb_intrinit(sc);
|
|
|
|
|
2008-06-25 15:42:32 +04:00
|
|
|
if (NULL != (csc = config_found_ia(self, "pcmciaslot", &caa,
|
|
|
|
cbbprint))) {
|
2007-02-04 07:59:39 +03:00
|
|
|
DPRINTF(("%s: found cardslot\n", __func__));
|
2008-06-25 15:42:32 +04:00
|
|
|
sc->sc_csc = device_private(csc);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2000-02-23 10:28:54 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* static void pccbb_chipinit(struct pccbb_softc *sc)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function initialize YENTA chip registers listed below:
|
2000-02-23 10:28:54 +03:00
|
|
|
* 1) PCI command reg,
|
|
|
|
* 2) PCI and CardBus latency timer,
|
2000-07-10 01:58:30 +04:00
|
|
|
* 3) route PCI interrupt,
|
|
|
|
* 4) close all memory and io windows.
|
2001-10-17 14:25:51 +04:00
|
|
|
* 5) turn off bus power.
|
2005-03-23 23:53:19 +03:00
|
|
|
* 6) card detect and power cycle interrupts on.
|
2001-10-17 14:25:51 +04:00
|
|
|
* 7) clear interrupt
|
2000-02-23 10:28:54 +03:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_chipinit(struct pccbb_softc *sc)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_chipset_tag_t pc = sc->sc_pc;
|
|
|
|
pcitag_t tag = sc->sc_tag;
|
2001-10-17 14:25:51 +04:00
|
|
|
bus_space_tag_t bmt = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t bmh = sc->sc_base_memh;
|
2007-11-16 21:36:51 +03:00
|
|
|
pcireg_t bcr, bhlc, cbctl, csr, lscp, mfunc, mrburst, slotctl, sockctl,
|
2008-02-02 03:31:25 +03:00
|
|
|
sysctrl;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* Set PCI command reg.
|
|
|
|
* Some laptop's BIOSes (i.e. TICO) do not enable CardBus chip.
|
|
|
|
*/
|
2007-08-11 04:31:04 +04:00
|
|
|
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
|
2000-03-12 07:34:29 +03:00
|
|
|
/* I believe it is harmless. */
|
2007-08-11 04:31:04 +04:00
|
|
|
csr |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
|
2000-03-12 07:34:29 +03:00
|
|
|
PCI_COMMAND_MASTER_ENABLE);
|
2008-05-28 01:32:47 +04:00
|
|
|
|
|
|
|
/* All O2 Micro chips have broken parity-error reporting
|
|
|
|
* until proven otherwise. The OZ6933 PCI-CardBus Bridge
|
|
|
|
* is known to have the defect---see PR kern/38698.
|
|
|
|
*/
|
|
|
|
if (sc->sc_chipset != CB_O2MICRO)
|
|
|
|
csr |= PCI_COMMAND_PARITY_ENABLE;
|
|
|
|
|
|
|
|
csr |= PCI_COMMAND_SERR_ENABLE;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-03-12 07:34:29 +03:00
|
|
|
* Set CardBus latency timer.
|
2000-02-05 21:20:08 +03:00
|
|
|
*/
|
2007-08-11 04:31:04 +04:00
|
|
|
lscp = pci_conf_read(pc, tag, PCI_CB_LSCP_REG);
|
|
|
|
if (PCI_CB_LATENCY(lscp) < 0x20) {
|
|
|
|
lscp &= ~(PCI_CB_LATENCY_MASK << PCI_CB_LATENCY_SHIFT);
|
|
|
|
lscp |= (0x20 << PCI_CB_LATENCY_SHIFT);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_LSCP_REG, lscp);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2000-03-12 07:34:29 +03:00
|
|
|
DPRINTF(("CardBus latency timer 0x%x (%x)\n",
|
2007-08-11 04:31:04 +04:00
|
|
|
PCI_CB_LATENCY(lscp), pci_conf_read(pc, tag, PCI_CB_LSCP_REG)));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-03-12 07:34:29 +03:00
|
|
|
* Set PCI latency timer.
|
2000-02-05 21:20:08 +03:00
|
|
|
*/
|
2007-08-11 04:31:04 +04:00
|
|
|
bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG);
|
|
|
|
if (PCI_LATTIMER(bhlc) < 0x10) {
|
|
|
|
bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
|
|
|
|
bhlc |= (0x10 << PCI_LATTIMER_SHIFT);
|
|
|
|
pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2000-03-12 07:34:29 +03:00
|
|
|
DPRINTF(("PCI latency timer 0x%x (%x)\n",
|
2007-08-11 04:31:04 +04:00
|
|
|
PCI_LATTIMER(bhlc), pci_conf_read(pc, tag, PCI_BHLC_REG)));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
|
2000-03-12 07:34:29 +03:00
|
|
|
/* Route functional interrupts to PCI. */
|
2007-08-11 04:31:04 +04:00
|
|
|
bcr = pci_conf_read(pc, tag, PCI_BRIDGE_CONTROL_REG);
|
|
|
|
bcr |= CB_BCR_INTR_IREQ_ENABLE; /* disable PCI Intr */
|
|
|
|
bcr |= CB_BCR_WRITE_POST_ENABLE; /* enable write post */
|
|
|
|
/* assert reset */
|
|
|
|
bcr |= PCI_BRIDGE_CONTROL_SECBR << PCI_BRIDGE_CONTROL_SHIFT;
|
2007-11-16 21:36:51 +03:00
|
|
|
/* Set master abort mode to 1, forward SERR# from secondary
|
|
|
|
* to primary, and detect parity errors on secondary.
|
|
|
|
*/
|
|
|
|
bcr |= PCI_BRIDGE_CONTROL_MABRT << PCI_BRIDGE_CONTROL_SHIFT;
|
|
|
|
bcr |= PCI_BRIDGE_CONTROL_SERR << PCI_BRIDGE_CONTROL_SHIFT;
|
|
|
|
bcr |= PCI_BRIDGE_CONTROL_PERE << PCI_BRIDGE_CONTROL_SHIFT;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(pc, tag, PCI_BRIDGE_CONTROL_REG, bcr);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-12 07:34:29 +03:00
|
|
|
switch (sc->sc_chipset) {
|
|
|
|
case CB_TI113X:
|
2007-08-11 04:31:04 +04:00
|
|
|
cbctl = pci_conf_read(pc, tag, PCI_CBCTRL);
|
2000-03-12 07:34:29 +03:00
|
|
|
/* This bit is shared, but may read as 0 on some chips, so set
|
|
|
|
it explicitly on both functions. */
|
2007-08-11 04:31:04 +04:00
|
|
|
cbctl |= PCI113X_CBCTRL_PCI_IRQ_ENA;
|
2000-02-05 21:20:08 +03:00
|
|
|
/* CSC intr enable */
|
2007-08-11 04:31:04 +04:00
|
|
|
cbctl |= PCI113X_CBCTRL_PCI_CSC;
|
2000-08-28 13:26:38 +04:00
|
|
|
/* functional intr prohibit | prohibit ISA routing */
|
2007-08-11 04:31:04 +04:00
|
|
|
cbctl &= ~(PCI113X_CBCTRL_PCI_INTR | PCI113X_CBCTRL_INT_MASK);
|
|
|
|
pci_conf_write(pc, tag, PCI_CBCTRL, cbctl);
|
2000-03-12 07:34:29 +03:00
|
|
|
break;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2007-11-16 21:36:51 +03:00
|
|
|
case CB_TI1420:
|
|
|
|
sysctrl = pci_conf_read(pc, tag, PCI_SYSCTRL);
|
|
|
|
mrburst = pccbb_burstup
|
|
|
|
? PCI1420_SYSCTRL_MRBURST : PCI1420_SYSCTRL_MRBURSTDN;
|
|
|
|
if ((sysctrl & PCI1420_SYSCTRL_MRBURST) == mrburst) {
|
|
|
|
printf("%s: %swrite bursts enabled\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev),
|
2007-11-16 21:36:51 +03:00
|
|
|
pccbb_burstup ? "read/" : "");
|
|
|
|
} else if (pccbb_burstup) {
|
|
|
|
printf("%s: enabling read/write bursts\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev));
|
2007-11-16 21:36:51 +03:00
|
|
|
sysctrl |= PCI1420_SYSCTRL_MRBURST;
|
|
|
|
pci_conf_write(pc, tag, PCI_SYSCTRL, sysctrl);
|
|
|
|
} else {
|
|
|
|
printf("%s: disabling read bursts, "
|
|
|
|
"enabling write bursts\n",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev));
|
2007-11-16 21:36:51 +03:00
|
|
|
sysctrl |= PCI1420_SYSCTRL_MRBURSTDN;
|
|
|
|
sysctrl &= ~PCI1420_SYSCTRL_MRBURSTUP;
|
|
|
|
pci_conf_write(pc, tag, PCI_SYSCTRL, sysctrl);
|
|
|
|
}
|
|
|
|
/*FALLTHROUGH*/
|
2000-12-09 00:51:02 +03:00
|
|
|
case CB_TI12XX:
|
2004-03-28 13:49:31 +04:00
|
|
|
/*
|
|
|
|
* Some TI 12xx (and [14][45]xx) based pci cards
|
|
|
|
* sometimes have issues with the MFUNC register not
|
|
|
|
* being initialized due to a bad EEPROM on board.
|
|
|
|
* Laptops that this matters on have this register
|
|
|
|
* properly initialized.
|
|
|
|
*
|
|
|
|
* The TI125X parts have a different register.
|
|
|
|
*/
|
2007-08-11 04:31:04 +04:00
|
|
|
mfunc = pci_conf_read(pc, tag, PCI12XX_MFUNC);
|
2010-12-27 22:02:32 +03:00
|
|
|
if ((mfunc & (PCI12XX_MFUNC_PIN0 | PCI12XX_MFUNC_PIN1)) == 0) {
|
|
|
|
/* Enable PCI interrupt /INTA */
|
2007-08-11 04:31:04 +04:00
|
|
|
mfunc |= PCI12XX_MFUNC_PIN0_INTA;
|
2010-12-27 22:02:32 +03:00
|
|
|
|
|
|
|
/* XXX this is TI1520 only */
|
2004-03-28 13:49:31 +04:00
|
|
|
if ((pci_conf_read(pc, tag, PCI_SYSCTRL) &
|
2010-12-27 22:02:32 +03:00
|
|
|
PCI12XX_SYSCTRL_INTRTIE) == 0)
|
|
|
|
/* Enable PCI interrupt /INTB */
|
2007-08-11 04:31:04 +04:00
|
|
|
mfunc |= PCI12XX_MFUNC_PIN1_INTB;
|
2010-12-27 22:02:32 +03:00
|
|
|
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(pc, tag, PCI12XX_MFUNC, mfunc);
|
2004-03-28 13:49:31 +04:00
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
|
|
|
|
case CB_TI125X:
|
|
|
|
/*
|
|
|
|
* Disable zoom video. Some machines initialize this
|
|
|
|
* improperly and experience has shown that this helps
|
|
|
|
* prevent strange behavior.
|
|
|
|
*/
|
|
|
|
pci_conf_write(pc, tag, PCI12XX_MMCTRL, 0);
|
|
|
|
|
2007-08-11 04:31:04 +04:00
|
|
|
sysctrl = pci_conf_read(pc, tag, PCI_SYSCTRL);
|
|
|
|
sysctrl |= PCI12XX_SYSCTRL_VCCPROT;
|
|
|
|
pci_conf_write(pc, tag, PCI_SYSCTRL, sysctrl);
|
|
|
|
cbctl = pci_conf_read(pc, tag, PCI_CBCTRL);
|
|
|
|
cbctl |= PCI12XX_CBCTRL_CSC;
|
|
|
|
pci_conf_write(pc, tag, PCI_CBCTRL, cbctl);
|
2000-12-09 00:51:02 +03:00
|
|
|
break;
|
|
|
|
|
2000-03-12 07:34:29 +03:00
|
|
|
case CB_TOPIC95B:
|
2007-08-11 04:31:04 +04:00
|
|
|
sockctl = pci_conf_read(pc, tag, TOPIC_SOCKET_CTRL);
|
|
|
|
sockctl |= TOPIC_SOCKET_CTRL_SCR_IRQSEL;
|
|
|
|
pci_conf_write(pc, tag, TOPIC_SOCKET_CTRL, sockctl);
|
|
|
|
slotctl = pci_conf_read(pc, tag, TOPIC_SLOT_CTRL);
|
2001-08-30 13:20:17 +04:00
|
|
|
DPRINTF(("%s: topic slot ctrl reg 0x%x -> ",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev), slotctl));
|
2007-08-11 04:31:04 +04:00
|
|
|
slotctl |= (TOPIC_SLOT_CTRL_SLOTON | TOPIC_SLOT_CTRL_SLOTEN |
|
2001-08-30 13:20:17 +04:00
|
|
|
TOPIC_SLOT_CTRL_ID_LOCK | TOPIC_SLOT_CTRL_CARDBUS);
|
2007-08-11 04:31:04 +04:00
|
|
|
slotctl &= ~TOPIC_SLOT_CTRL_SWDETECT;
|
|
|
|
DPRINTF(("0x%x\n", slotctl));
|
|
|
|
pci_conf_write(pc, tag, TOPIC_SLOT_CTRL, slotctl);
|
2001-08-30 13:20:17 +04:00
|
|
|
break;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2001-08-30 13:20:17 +04:00
|
|
|
case CB_TOPIC97:
|
2007-08-11 04:31:04 +04:00
|
|
|
slotctl = pci_conf_read(pc, tag, TOPIC_SLOT_CTRL);
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("%s: topic slot ctrl reg 0x%x -> ",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev), slotctl));
|
2007-08-11 04:31:04 +04:00
|
|
|
slotctl |= (TOPIC_SLOT_CTRL_SLOTON | TOPIC_SLOT_CTRL_SLOTEN |
|
2000-03-12 07:34:29 +03:00
|
|
|
TOPIC_SLOT_CTRL_ID_LOCK | TOPIC_SLOT_CTRL_CARDBUS);
|
2007-08-11 04:31:04 +04:00
|
|
|
slotctl &= ~TOPIC_SLOT_CTRL_SWDETECT;
|
|
|
|
slotctl |= TOPIC97_SLOT_CTRL_PCIINT;
|
|
|
|
slotctl &= ~(TOPIC97_SLOT_CTRL_STSIRQP | TOPIC97_SLOT_CTRL_IRQP);
|
|
|
|
DPRINTF(("0x%x\n", slotctl));
|
|
|
|
pci_conf_write(pc, tag, TOPIC_SLOT_CTRL, slotctl);
|
2001-10-17 14:25:51 +04:00
|
|
|
/* make sure to assert LV card support bits */
|
|
|
|
bus_space_write_1(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
0x800 + 0x3e,
|
|
|
|
bus_space_read_1(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
0x800 + 0x3e) | 0x03);
|
2000-03-12 07:34:29 +03:00
|
|
|
break;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-12 07:34:29 +03:00
|
|
|
/* Close all memory and I/O windows. */
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(pc, tag, PCI_CB_MEMBASE0, 0xffffffff);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_MEMLIMIT0, 0);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_MEMBASE1, 0xffffffff);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_MEMLIMIT1, 0);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_IOBASE0, 0xffffffff);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_IOLIMIT0, 0);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_IOBASE1, 0xffffffff);
|
|
|
|
pci_conf_write(pc, tag, PCI_CB_IOLIMIT1, 0);
|
2000-10-25 13:15:58 +04:00
|
|
|
|
|
|
|
/* reset 16-bit pcmcia bus */
|
2001-10-17 14:25:51 +04:00
|
|
|
bus_space_write_1(bmt, bmh, 0x800 + PCIC_INTR,
|
|
|
|
bus_space_read_1(bmt, bmh, 0x800 + PCIC_INTR) & ~PCIC_INTR_RESET);
|
2000-10-25 13:15:58 +04:00
|
|
|
|
2001-10-17 14:25:51 +04:00
|
|
|
/* turn off power */
|
2008-01-03 02:11:34 +03:00
|
|
|
pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V);
|
2008-02-02 03:31:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pccbb_intrinit(struct pccbb_softc *sc)
|
|
|
|
{
|
|
|
|
pcireg_t sockmask;
|
|
|
|
const char *intrstr = NULL;
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
pci_chipset_tag_t pc = sc->sc_pc;
|
|
|
|
bus_space_tag_t bmt = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t bmh = sc->sc_base_memh;
|
|
|
|
|
|
|
|
/* Map and establish the interrupt. */
|
|
|
|
if (pci_intr_map(&sc->sc_pa, &ih)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "couldn't map interrupt\n");
|
2008-02-02 03:31:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
intrstr = pci_intr_string(pc, ih);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX pccbbintr should be called under the priority lower
|
2009-03-05 04:38:12 +03:00
|
|
|
* than any other hard interrupts.
|
2008-02-02 03:31:25 +03:00
|
|
|
*/
|
|
|
|
KASSERT(sc->sc_ih == NULL);
|
|
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, pccbbintr, sc);
|
|
|
|
|
|
|
|
if (sc->sc_ih == NULL) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "couldn't establish interrupt");
|
2008-02-02 03:31:25 +03:00
|
|
|
if (intrstr != NULL)
|
|
|
|
aprint_error(" at %s\n", intrstr);
|
|
|
|
else
|
|
|
|
aprint_error("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
|
2001-10-17 14:25:51 +04:00
|
|
|
|
2005-03-23 23:53:19 +03:00
|
|
|
/* CSC Interrupt: Card detect and power cycle interrupts on */
|
2007-08-11 04:31:04 +04:00
|
|
|
sockmask = bus_space_read_4(bmt, bmh, CB_SOCKET_MASK);
|
2008-02-02 03:31:25 +03:00
|
|
|
sockmask |= CB_SOCKET_MASK_CSTS | CB_SOCKET_MASK_CD |
|
|
|
|
CB_SOCKET_MASK_POWER;
|
2007-08-11 04:31:04 +04:00
|
|
|
bus_space_write_4(bmt, bmh, CB_SOCKET_MASK, sockmask);
|
2001-10-17 14:25:51 +04:00
|
|
|
/* reset interrupt */
|
|
|
|
bus_space_write_4(bmt, bmh, CB_SOCKET_EVENT,
|
|
|
|
bus_space_read_4(bmt, bmh, CB_SOCKET_EVENT));
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
2000-02-23 10:28:54 +03:00
|
|
|
* STATIC void pccbb_pcmcia_attach_setup(struct pccbb_softc *sc,
|
|
|
|
* struct pcmciabus_attach_args *paa)
|
|
|
|
*
|
|
|
|
* This function attaches 16-bit PCcard bus.
|
1999-10-19 13:29:46 +04:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_attach_setup(struct pccbb_softc *sc,
|
|
|
|
struct pcmciabus_attach_args *paa)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-01-12 12:23:26 +03:00
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_tag_t rb;
|
2000-01-12 12:23:26 +03:00
|
|
|
#endif
|
2000-03-12 14:31:53 +03:00
|
|
|
/*
|
|
|
|
* We need to do a few things here:
|
|
|
|
* 1) Disable routing of CSC and functional interrupts to ISA IRQs by
|
|
|
|
* setting the IRQ numbers to 0.
|
|
|
|
* 2) Set bit 4 of PCIC_INTR, which is needed on some chips to enable
|
|
|
|
* routing of CSC interrupts (e.g. card removal) to PCI while in
|
|
|
|
* PCMCIA mode. We just leave this set all the time.
|
|
|
|
* 3) Enable card insertion/removal interrupts in case the chip also
|
|
|
|
* needs that while in PCMCIA mode.
|
|
|
|
* 4) Clear any pending CSC interrupt.
|
|
|
|
*/
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_INTR, PCIC_INTR_ENABLE);
|
2000-08-28 13:26:38 +04:00
|
|
|
if (sc->sc_chipset == CB_TI113X) {
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_CSC_INTR, 0);
|
2000-08-28 13:26:38 +04:00
|
|
|
} else {
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_CSC_INTR, PCIC_CSC_INTR_CD_ENABLE);
|
|
|
|
Pcic_read(sc, PCIC_CSC);
|
2000-08-28 13:26:38 +04:00
|
|
|
}
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2000-03-14 04:29:30 +03:00
|
|
|
/* initialize pcmcia bus attachment */
|
2000-02-05 21:20:08 +03:00
|
|
|
paa->paa_busname = "pcmcia";
|
2008-06-27 00:57:10 +04:00
|
|
|
paa->pct = &pccbb_pcmcia_funcs;
|
|
|
|
paa->pch = sc;
|
2000-01-12 12:23:26 +03:00
|
|
|
#if rbus
|
2008-06-25 19:29:23 +04:00
|
|
|
rb = sc->sc_rbus_iot;
|
2000-01-12 12:23:26 +03:00
|
|
|
#endif
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* int pccbbintr(arg)
|
|
|
|
* void *arg;
|
|
|
|
* This routine handles the interrupt from Yenta PCI-CardBus bridge
|
|
|
|
* itself.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbbintr(void *arg)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)arg;
|
2009-05-21 21:32:32 +04:00
|
|
|
struct cardslot_softc *csc;
|
2000-03-12 14:31:53 +03:00
|
|
|
u_int32_t sockevent, sockstate;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_tag_t memt = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t memh = sc->sc_base_memh;
|
|
|
|
|
2008-06-25 15:42:32 +04:00
|
|
|
if (!device_has_power(sc->sc_dev))
|
2008-02-02 03:31:25 +03:00
|
|
|
return 0;
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sockevent = bus_space_read_4(memt, memh, CB_SOCKET_EVENT);
|
2000-03-12 14:31:53 +03:00
|
|
|
bus_space_write_4(memt, memh, CB_SOCKET_EVENT, sockevent);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_read(sc, PCIC_CSC);
|
2000-03-12 14:31:53 +03:00
|
|
|
|
2007-11-21 05:07:09 +03:00
|
|
|
if (sockevent != 0) {
|
2011-01-04 13:40:17 +03:00
|
|
|
DPRINTF(("%s: enter sockevent %" PRIx32 "\n",
|
|
|
|
__func__, sockevent));
|
2007-11-21 05:07:09 +03:00
|
|
|
}
|
|
|
|
|
2009-02-06 04:15:53 +03:00
|
|
|
/* XXX sockevent == CB_SOCKET_EVENT_CSTS|CB_SOCKET_EVENT_POWER
|
|
|
|
* does occur in the wild. Check for a _POWER event before
|
|
|
|
* possibly exiting because of an _CSTS event.
|
|
|
|
*/
|
|
|
|
if (sockevent & CB_SOCKET_EVENT_POWER) {
|
|
|
|
DPRINTF(("Powercycling because of socket event\n"));
|
|
|
|
/* XXX: Does not happen when attaching a 16-bit card */
|
2009-07-24 01:22:25 +04:00
|
|
|
mutex_enter(&sc->sc_pwr_mtx);
|
2009-02-06 04:15:53 +03:00
|
|
|
sc->sc_pwrcycle++;
|
2009-07-24 01:22:25 +04:00
|
|
|
cv_signal(&sc->sc_pwr_cv);
|
|
|
|
mutex_exit(&sc->sc_pwr_mtx);
|
2009-02-06 04:15:53 +03:00
|
|
|
}
|
|
|
|
|
2007-11-21 05:07:09 +03:00
|
|
|
/* Sometimes a change of CSTSCHG# accompanies the first
|
|
|
|
* interrupt from an Atheros WLAN. That generates a
|
|
|
|
* CB_SOCKET_EVENT_CSTS event on the bridge. The event
|
|
|
|
* isn't interesting to pccbb(4), so we used to ignore the
|
|
|
|
* interrupt. Now, let the child devices try to handle
|
|
|
|
* the interrupt, instead. The Atheros NIC produces
|
|
|
|
* interrupts more reliably, now: used to be that it would
|
|
|
|
* only interrupt if the driver avoided powering down the
|
|
|
|
* NIC's cardslot, and then the NIC would only work after
|
|
|
|
* it was reset a second time.
|
|
|
|
*/
|
|
|
|
if (sockevent == 0 ||
|
|
|
|
(sockevent & ~(CB_SOCKET_EVENT_POWER|CB_SOCKET_EVENT_CD)) != 0) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* This intr is not for me: it may be for my child devices. */
|
2000-04-06 13:11:57 +04:00
|
|
|
if (sc->sc_pil_intr_enable) {
|
|
|
|
return pccbbintr_function(sc);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sockevent & CB_SOCKET_EVENT_CD) {
|
2000-03-12 14:31:53 +03:00
|
|
|
sockstate = bus_space_read_4(memt, memh, CB_SOCKET_STAT);
|
2003-06-19 14:48:58 +04:00
|
|
|
if (0x00 != (sockstate & CB_SOCKET_STAT_CD)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* A card should be removed. */
|
|
|
|
if (sc->sc_flags & CBB_CARDEXIST) {
|
2008-02-02 00:13:44 +03:00
|
|
|
DPRINTF(("%s: 0x%08x",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev), sockevent));
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF((" card removed, 0x%08x\n", sockstate));
|
|
|
|
sc->sc_flags &= ~CBB_CARDEXIST;
|
2009-05-21 21:32:32 +04:00
|
|
|
if ((csc = sc->sc_csc) == NULL)
|
|
|
|
;
|
|
|
|
else if (csc->sc_status &
|
2000-03-14 13:20:09 +03:00
|
|
|
CARDSLOT_STATUS_CARD_16) {
|
2009-05-21 21:32:32 +04:00
|
|
|
cardslot_event_throw(csc,
|
2000-02-05 21:20:08 +03:00
|
|
|
CARDSLOT_EVENT_REMOVAL_16);
|
2009-05-21 21:32:32 +04:00
|
|
|
} else if (csc->sc_status &
|
2000-03-14 13:20:09 +03:00
|
|
|
CARDSLOT_STATUS_CARD_CB) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* Cardbus intr removed */
|
2009-05-21 21:32:32 +04:00
|
|
|
cardslot_event_throw(csc,
|
2000-02-05 21:20:08 +03:00
|
|
|
CARDSLOT_EVENT_REMOVAL_CB);
|
|
|
|
}
|
2002-01-10 13:30:08 +03:00
|
|
|
} else if (sc->sc_flags & CBB_INSERTING) {
|
|
|
|
sc->sc_flags &= ~CBB_INSERTING;
|
|
|
|
callout_stop(&sc->sc_insert_ch);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2000-03-14 13:23:16 +03:00
|
|
|
} else if (0x00 == (sockstate & CB_SOCKET_STAT_CD) &&
|
|
|
|
/*
|
|
|
|
* The pccbbintr may called from powerdown hook when
|
|
|
|
* the system resumed, to detect the card
|
|
|
|
* insertion/removal during suspension.
|
|
|
|
*/
|
|
|
|
(sc->sc_flags & CBB_CARDEXIST) == 0) {
|
2000-02-05 21:20:08 +03:00
|
|
|
if (sc->sc_flags & CBB_INSERTING) {
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_stop(&sc->sc_insert_ch);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2009-07-24 01:22:25 +04:00
|
|
|
callout_schedule(&sc->sc_insert_ch, mstohz(200));
|
2000-02-05 21:20:08 +03:00
|
|
|
sc->sc_flags |= CBB_INSERTING;
|
|
|
|
}
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-14 13:20:09 +03:00
|
|
|
return (1);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2000-01-26 12:02:41 +03:00
|
|
|
/*
|
|
|
|
* static int pccbbintr_function(struct pccbb_softc *sc)
|
|
|
|
*
|
|
|
|
* This function calls each interrupt handler registered at the
|
2000-03-14 04:29:30 +03:00
|
|
|
* bridge. The interrupt handlers are called in registered order.
|
2000-01-26 12:02:41 +03:00
|
|
|
*/
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbbintr_function(struct pccbb_softc *sc)
|
2000-01-26 12:02:41 +03:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
int retval = 0, val;
|
|
|
|
struct pccbb_intrhand_list *pil;
|
2006-12-21 18:55:21 +03:00
|
|
|
int s;
|
2000-01-26 12:02:41 +03:00
|
|
|
|
2007-12-20 23:48:24 +03:00
|
|
|
LIST_FOREACH(pil, &sc->sc_pil, pil_next) {
|
2006-12-21 18:55:21 +03:00
|
|
|
s = splraiseipl(pil->pil_icookie);
|
2000-06-08 14:28:28 +04:00
|
|
|
val = (*pil->pil_func)(pil->pil_arg);
|
2006-12-21 18:55:21 +03:00
|
|
|
splx(s);
|
2000-06-08 14:28:28 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
retval = retval == 1 ? 1 :
|
|
|
|
retval == 0 ? val : val != 0 ? val : retval;
|
|
|
|
}
|
2000-01-26 12:02:41 +03:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return retval;
|
2000-01-26 12:02:41 +03:00
|
|
|
}
|
|
|
|
|
1999-10-15 10:07:17 +04:00
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
pci113x_insert(void *arg)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-25 15:42:32 +04:00
|
|
|
struct pccbb_softc *sc = arg;
|
2009-05-21 21:32:32 +04:00
|
|
|
struct cardslot_softc *csc;
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int32_t sockevent, sockstate;
|
|
|
|
|
2002-01-10 13:30:08 +03:00
|
|
|
if (!(sc->sc_flags & CBB_INSERTING)) {
|
|
|
|
/* We add a card only under inserting state. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sc->sc_flags &= ~CBB_INSERTING;
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
sockevent = bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
CB_SOCKET_EVENT);
|
|
|
|
sockstate = bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
CB_SOCKET_STAT);
|
|
|
|
|
|
|
|
if (0 == (sockstate & CB_SOCKET_STAT_CD)) { /* card exist */
|
2008-06-25 15:42:32 +04:00
|
|
|
DPRINTF(("%s: 0x%08x", device_xname(sc->sc_dev), sockevent));
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF((" card inserted, 0x%08x\n", sockstate));
|
|
|
|
sc->sc_flags |= CBB_CARDEXIST;
|
2000-03-14 04:29:30 +03:00
|
|
|
/* call pccard interrupt handler here */
|
2009-05-21 21:32:32 +04:00
|
|
|
if ((csc = sc->sc_csc) == NULL)
|
|
|
|
;
|
|
|
|
else if (sockstate & CB_SOCKET_STAT_16BIT) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* 16-bit card found */
|
2009-05-21 21:32:32 +04:00
|
|
|
cardslot_event_throw(csc, CARDSLOT_EVENT_INSERTION_16);
|
2000-02-05 21:20:08 +03:00
|
|
|
} else if (sockstate & CB_SOCKET_STAT_CB) {
|
2000-03-14 04:29:30 +03:00
|
|
|
/* cardbus card found */
|
2009-05-21 21:32:32 +04:00
|
|
|
cardslot_event_throw(csc, CARDSLOT_EVENT_INSERTION_CB);
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
|
|
|
/* who are you? */
|
|
|
|
}
|
|
|
|
} else {
|
2009-07-24 01:22:25 +04:00
|
|
|
callout_schedule(&sc->sc_insert_ch, mstohz(100));
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define PCCBB_PCMCIA_OFFSET 0x800
|
|
|
|
static u_int8_t
|
2008-06-25 19:29:23 +04:00
|
|
|
pccbb_pcmcia_read(struct pccbb_softc *sc, int reg)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-25 19:29:23 +04:00
|
|
|
bus_space_barrier(sc->sc_base_memt, sc->sc_base_memh,
|
2000-11-28 12:11:36 +03:00
|
|
|
PCCBB_PCMCIA_OFFSET + reg, 1, BUS_SPACE_BARRIER_READ);
|
|
|
|
|
2008-06-25 19:29:23 +04:00
|
|
|
return bus_space_read_1(sc->sc_base_memt, sc->sc_base_memh,
|
2000-02-05 21:20:08 +03:00
|
|
|
PCCBB_PCMCIA_OFFSET + reg);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-06-25 19:29:23 +04:00
|
|
|
pccbb_pcmcia_write(struct pccbb_softc *sc, int reg, u_int8_t val)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-25 19:29:23 +04:00
|
|
|
bus_space_write_1(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
PCCBB_PCMCIA_OFFSET + reg, val);
|
2000-11-28 12:11:36 +03:00
|
|
|
|
2008-06-25 19:29:23 +04:00
|
|
|
bus_space_barrier(sc->sc_base_memt, sc->sc_base_memh,
|
2000-11-28 12:11:36 +03:00
|
|
|
PCCBB_PCMCIA_OFFSET + reg, 1, BUS_SPACE_BARRIER_WRITE);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_ctrl(cardbus_chipset_tag_t, int)
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_ctrl(cardbus_chipset_tag_t ct, int command)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case CARDBUS_CD:
|
|
|
|
if (2 == pccbb_detect_card(sc)) {
|
|
|
|
int retval = 0;
|
|
|
|
int status = cb_detect_voltage(sc);
|
|
|
|
if (PCCARD_VCC_5V & status) {
|
|
|
|
retval |= CARDBUS_5V_CARD;
|
|
|
|
}
|
|
|
|
if (PCCARD_VCC_3V & status) {
|
|
|
|
retval |= CARDBUS_3V_CARD;
|
|
|
|
}
|
|
|
|
if (PCCARD_VCC_XV & status) {
|
|
|
|
retval |= CARDBUS_XV_CARD;
|
|
|
|
}
|
|
|
|
if (PCCARD_VCC_YV & status) {
|
|
|
|
retval |= CARDBUS_YV_CARD;
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case CARDBUS_RESET:
|
|
|
|
return cb_reset(sc);
|
|
|
|
case CARDBUS_IO_ENABLE: /* fallthrough */
|
|
|
|
case CARDBUS_IO_DISABLE: /* fallthrough */
|
|
|
|
case CARDBUS_MEM_ENABLE: /* fallthrough */
|
|
|
|
case CARDBUS_MEM_DISABLE: /* fallthrough */
|
|
|
|
case CARDBUS_BM_ENABLE: /* fallthrough */
|
|
|
|
case CARDBUS_BM_DISABLE: /* fallthrough */
|
2001-10-17 14:25:51 +04:00
|
|
|
/* XXX: I think we don't need to call this function below. */
|
2000-02-05 21:20:08 +03:00
|
|
|
return pccbb_cardenable(sc, command);
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2008-01-03 02:11:34 +03:00
|
|
|
STATIC int
|
|
|
|
pccbb_power_ct(cardbus_chipset_tag_t ct, int command)
|
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
|
|
|
|
return pccbb_power(sc, command);
|
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_power(cardbus_chipset_tag_t, int)
|
|
|
|
* This function returns true when it succeeds and returns false when
|
|
|
|
* it fails.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2008-01-03 02:11:34 +03:00
|
|
|
pccbb_power(struct pccbb_softc *sc, int command)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
u_int32_t status, osock_ctrl, sock_ctrl, reg_ctrl;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_tag_t memt = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t memh = sc->sc_base_memh;
|
2009-07-24 01:22:25 +04:00
|
|
|
int on = 0, pwrcycle, times;
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
struct timeval before, after, diff;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_power: %s and %s [0x%x]\n",
|
2000-02-05 21:20:08 +03:00
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_UC ? "CARDBUS_VCC_UC" :
|
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_5V ? "CARDBUS_VCC_5V" :
|
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_3V ? "CARDBUS_VCC_3V" :
|
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_XV ? "CARDBUS_VCC_XV" :
|
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_YV ? "CARDBUS_VCC_YV" :
|
|
|
|
(command & CARDBUS_VCCMASK) == CARDBUS_VCC_0V ? "CARDBUS_VCC_0V" :
|
|
|
|
"UNKNOWN",
|
|
|
|
(command & CARDBUS_VPPMASK) == CARDBUS_VPP_UC ? "CARDBUS_VPP_UC" :
|
|
|
|
(command & CARDBUS_VPPMASK) == CARDBUS_VPP_12V ? "CARDBUS_VPP_12V" :
|
|
|
|
(command & CARDBUS_VPPMASK) == CARDBUS_VPP_VCC ? "CARDBUS_VPP_VCC" :
|
|
|
|
(command & CARDBUS_VPPMASK) == CARDBUS_VPP_0V ? "CARDBUS_VPP_0V" :
|
|
|
|
"UNKNOWN", command));
|
|
|
|
|
|
|
|
status = bus_space_read_4(memt, memh, CB_SOCKET_STAT);
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
osock_ctrl = sock_ctrl = bus_space_read_4(memt, memh, CB_SOCKET_CTRL);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
|
|
|
switch (command & CARDBUS_VCCMASK) {
|
|
|
|
case CARDBUS_VCC_UC:
|
|
|
|
break;
|
|
|
|
case CARDBUS_VCC_5V:
|
2005-01-16 11:51:55 +03:00
|
|
|
on++;
|
2000-02-05 21:20:08 +03:00
|
|
|
if (CB_SOCKET_STAT_5VCARD & status) { /* check 5 V card */
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK;
|
|
|
|
sock_ctrl |= CB_SOCKET_CTRL_VCC_5V;
|
|
|
|
} else {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"BAD voltage request: no 5 V card\n");
|
2003-10-23 04:04:03 +04:00
|
|
|
return 0;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CARDBUS_VCC_3V:
|
2005-01-16 11:51:55 +03:00
|
|
|
on++;
|
2000-02-05 21:20:08 +03:00
|
|
|
if (CB_SOCKET_STAT_3VCARD & status) {
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK;
|
|
|
|
sock_ctrl |= CB_SOCKET_CTRL_VCC_3V;
|
|
|
|
} else {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"BAD voltage request: no 3.3 V card\n");
|
2003-10-23 04:04:03 +04:00
|
|
|
return 0;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CARDBUS_VCC_0V:
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0; /* power NEVER changed */
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
switch (command & CARDBUS_VPPMASK) {
|
|
|
|
case CARDBUS_VPP_UC:
|
|
|
|
break;
|
|
|
|
case CARDBUS_VPP_0V:
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK;
|
|
|
|
break;
|
|
|
|
case CARDBUS_VPP_VCC:
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK;
|
|
|
|
sock_ctrl |= ((sock_ctrl >> 4) & 0x07);
|
|
|
|
break;
|
|
|
|
case CARDBUS_VPP_12V:
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK;
|
|
|
|
sock_ctrl |= CB_SOCKET_CTRL_VPP_12V;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_debug_dev(sc->sc_dev, "osock_ctrl %#" PRIx32
|
2008-02-02 00:13:44 +03:00
|
|
|
" sock_ctrl %#" PRIx32 "\n", osock_ctrl, sock_ctrl);
|
2005-01-16 11:51:55 +03:00
|
|
|
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
microtime(&before);
|
2009-07-24 01:22:25 +04:00
|
|
|
mutex_enter(&sc->sc_pwr_mtx);
|
|
|
|
pwrcycle = sc->sc_pwrcycle;
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_write_4(memt, memh, CB_SOCKET_CTRL, sock_ctrl);
|
2005-01-16 11:51:55 +03:00
|
|
|
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
/*
|
|
|
|
* Wait as long as 200ms for a power-cycle interrupt. If
|
|
|
|
* interrupts are enabled, but the socket has already
|
|
|
|
* changed to the desired status, keep waiting for the
|
|
|
|
* interrupt. "Consuming" the interrupt in this way keeps
|
|
|
|
* the interrupt from prematurely waking some subsequent
|
|
|
|
* pccbb_power call.
|
|
|
|
*
|
|
|
|
* XXX Not every bridge interrupts on the ->OFF transition.
|
|
|
|
* XXX That's ok, we will time-out after 200ms.
|
|
|
|
*
|
|
|
|
* XXX The power cycle event will never happen when attaching
|
|
|
|
* XXX a 16-bit card. That's ok, we will time-out after
|
|
|
|
* XXX 200ms.
|
|
|
|
*/
|
|
|
|
for (times = 5; --times >= 0; ) {
|
|
|
|
if (cold)
|
|
|
|
DELAY(40 * 1000);
|
|
|
|
else {
|
2009-07-24 01:22:25 +04:00
|
|
|
(void)cv_timedwait(&sc->sc_pwr_cv, &sc->sc_pwr_mtx,
|
|
|
|
mstohz(40));
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
if (pwrcycle == sc->sc_pwrcycle)
|
|
|
|
continue;
|
2005-03-23 23:53:19 +03:00
|
|
|
}
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
status = bus_space_read_4(memt, memh, CB_SOCKET_STAT);
|
|
|
|
if ((status & CB_SOCKET_STAT_PWRCYCLE) != 0 && on)
|
|
|
|
break;
|
|
|
|
if ((status & CB_SOCKET_STAT_PWRCYCLE) == 0 && !on)
|
|
|
|
break;
|
2005-01-16 11:51:55 +03:00
|
|
|
}
|
2009-07-24 01:22:25 +04:00
|
|
|
mutex_exit(&sc->sc_pwr_mtx);
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
microtime(&after);
|
|
|
|
timersub(&after, &before, &diff);
|
2009-01-11 05:45:45 +03:00
|
|
|
aprint_debug_dev(sc->sc_dev, "wait took%s %lld.%06lds\n",
|
|
|
|
(on && times < 0) ? " too long" : "", (long long)diff.tv_sec,
|
|
|
|
(long)diff.tv_usec);
|
Fix a bug in Cardbus power activation.
Most Cardbus bridges supported by pccbb(4) fire a power-cycle
interrupt when the power state of a cardslot changes from 'off' to
'on'. TI bridges fire a power-cycle interrupt on both on->off and
off->on changes.
When pccbb_power() powered-down a cardslot, it did not wait around
for the power-cycle interrupt. When pccbb_power() powered-up a
cardslot, it did wait for the interrupt. If a pccbb_power(UP)
followed a pccbb_power(DOWN) very closely, pccbb_power() used to
interpret the power-cycle interrupt for the up->down transition as
"power-up complete," read the power-state bit and, finding that
power had NOT been activated, complain, "cbb0: power on failed?"
Then pccbb_power() exited before power-activation was complete,
falsely indicating that the power-activation *was* complete. After
that, a driver attach/enable routine would blithely configure a
card that was not fully powered-up. An operator who ran a command
such as 'ifconfig rtw0 down up' or 'ifconfig ath0 down up' would
read 'cbb0: power on failed?' in the system log, and their NIC
would misbehave.
This excerpt from a comment in the source should suffice to explain
how I fixed the bug,
/*
* Wait as long as 200ms for a power-cycle interrupt. If
* interrupts are enabled, but the socket has already
* changed to the desired status, keep waiting for the
* interrupt. "Consuming" the interrupt in this way keeps
* the interrupt from prematurely waking some subsequent
* pccbb_power call.
And this explains why this patch will work for Ricoh bridges that
do not fire an interrupt on the on->off transition:
* XXX Not every bridge interrupts on the ->OFF transition.
* XXX That's ok, we will time-out after 200ms.
*
* XXX The power cycle event will never happen when attaching
* XXX a 16-bit card. That's ok, we will time-out after
* XXX 200ms.
*/
M. Warner Losh and Charles M. Hannum provided valuable input on
this patch.
2007-02-05 00:04:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, wait a bit longer for things to settle.
|
|
|
|
*/
|
|
|
|
if (on && sc->sc_chipset == CB_TOPIC95B)
|
|
|
|
delay_ms(100, sc);
|
2005-01-16 11:51:55 +03:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
status = bus_space_read_4(memt, memh, CB_SOCKET_STAT);
|
|
|
|
|
2006-07-09 00:20:27 +04:00
|
|
|
if (on && sc->sc_chipset != CB_TOPIC95B) {
|
2005-01-16 11:51:55 +03:00
|
|
|
if ((status & CB_SOCKET_STAT_PWRCYCLE) == 0)
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "power on failed?\n");
|
2005-01-16 11:51:55 +03:00
|
|
|
}
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (status & CB_SOCKET_STAT_BADVCC) { /* bad Vcc request */
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"bad Vcc request. sock_ctrl 0x%x, sock_status 0x%x\n",
|
|
|
|
sock_ctrl, status);
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "disabling socket\n");
|
2004-08-12 11:15:49 +04:00
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VCCMASK;
|
|
|
|
sock_ctrl &= ~CB_SOCKET_CTRL_VPPMASK;
|
|
|
|
bus_space_write_4(memt, memh, CB_SOCKET_CTRL, sock_ctrl);
|
2005-01-16 11:51:55 +03:00
|
|
|
status &= ~CB_SOCKET_STAT_BADVCC;
|
2007-07-16 18:36:01 +04:00
|
|
|
bus_space_write_4(memt, memh, CB_SOCKET_FORCE, status);
|
2004-08-12 11:15:49 +04:00
|
|
|
printf("new status 0x%x\n", bus_space_read_4(memt, memh,
|
|
|
|
CB_SOCKET_STAT));
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2000-11-28 12:11:36 +03:00
|
|
|
|
2002-05-31 17:34:03 +04:00
|
|
|
if (sc->sc_chipset == CB_TOPIC97) {
|
|
|
|
reg_ctrl = pci_conf_read(sc->sc_pc, sc->sc_tag, TOPIC_REG_CTRL);
|
|
|
|
reg_ctrl &= ~TOPIC97_REG_CTRL_TESTMODE;
|
|
|
|
if ((command & CARDBUS_VCCMASK) == CARDBUS_VCC_0V)
|
|
|
|
reg_ctrl &= ~TOPIC97_REG_CTRL_CLKRUN_ENA;
|
|
|
|
else
|
|
|
|
reg_ctrl |= TOPIC97_REG_CTRL_CLKRUN_ENA;
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, TOPIC_REG_CTRL, reg_ctrl);
|
|
|
|
}
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 1; /* power changed correctly */
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* static int pccbb_detect_card(struct pccbb_softc *sc)
|
|
|
|
* return value: 0 if no card exists.
|
|
|
|
* 1 if 16-bit card exists.
|
|
|
|
* 2 if cardbus card exists.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_detect_card(struct pccbb_softc *sc)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_handle_t base_memh = sc->sc_base_memh;
|
|
|
|
bus_space_tag_t base_memt = sc->sc_base_memt;
|
|
|
|
u_int32_t sockstat =
|
|
|
|
bus_space_read_4(base_memt, base_memh, CB_SOCKET_STAT);
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
/* CD1 and CD2 asserted */
|
|
|
|
if (0x00 == (sockstat & CB_SOCKET_STAT_CD)) {
|
|
|
|
/* card must be present */
|
|
|
|
if (!(CB_SOCKET_STAT_NOTCARD & sockstat)) {
|
|
|
|
/* NOTACARD DEASSERTED */
|
|
|
|
if (CB_SOCKET_STAT_CB & sockstat) {
|
|
|
|
/* CardBus mode */
|
|
|
|
retval = 2;
|
|
|
|
} else if (CB_SOCKET_STAT_16BIT & sockstat) {
|
|
|
|
/* 16-bit mode */
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int cb_reset(struct pccbb_softc *sc)
|
|
|
|
* This function resets CardBus card.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
cb_reset(struct pccbb_softc *sc)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
|
|
|
* Reset Assert at least 20 ms
|
2000-02-05 21:20:08 +03:00
|
|
|
* Some machines request longer duration.
|
|
|
|
*/
|
|
|
|
int reset_duration =
|
2006-10-24 18:16:39 +04:00
|
|
|
(sc->sc_chipset == CB_RX5C47X ? 400 : 50);
|
2007-08-11 04:31:04 +04:00
|
|
|
u_int32_t bcr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG);
|
2007-11-24 10:53:52 +03:00
|
|
|
aprint_debug("%s: enter bcr %" PRIx32 "\n", __func__, bcr);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2000-06-07 13:02:46 +04:00
|
|
|
/* Reset bit Assert (bit 6 at 0x3E) */
|
2007-11-24 10:53:52 +03:00
|
|
|
bcr |= PCI_BRIDGE_CONTROL_SECBR << PCI_BRIDGE_CONTROL_SHIFT;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG, bcr);
|
2007-11-24 10:53:52 +03:00
|
|
|
aprint_debug("%s: wrote bcr %" PRIx32 "\n", __func__, bcr);
|
2007-02-04 08:08:18 +03:00
|
|
|
delay_ms(reset_duration, sc);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
|
|
|
if (CBB_CARDEXIST & sc->sc_flags) { /* A card exists. Reset it! */
|
2000-06-07 13:02:46 +04:00
|
|
|
/* Reset bit Deassert (bit 6 at 0x3E) */
|
2007-11-24 10:53:52 +03:00
|
|
|
bcr &= ~(PCI_BRIDGE_CONTROL_SECBR << PCI_BRIDGE_CONTROL_SHIFT);
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG,
|
|
|
|
bcr);
|
|
|
|
aprint_debug("%s: wrote bcr %" PRIx32 "\n", __func__, bcr);
|
2007-02-04 08:08:18 +03:00
|
|
|
delay_ms(reset_duration, sc);
|
2007-11-24 10:53:52 +03:00
|
|
|
aprint_debug("%s: end of delay\n", __func__);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
/* No card found on the slot. Keep Reset. */
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int cb_detect_voltage(struct pccbb_softc *sc)
|
|
|
|
* This function detect card Voltage.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
cb_detect_voltage(struct pccbb_softc *sc)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int32_t psr; /* socket present-state reg */
|
|
|
|
bus_space_tag_t iot = sc->sc_base_memt;
|
|
|
|
bus_space_handle_t ioh = sc->sc_base_memh;
|
|
|
|
int vol = PCCARD_VCC_UKN; /* set 0 */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
psr = bus_space_read_4(iot, ioh, CB_SOCKET_STAT);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (0x400u & psr) {
|
|
|
|
vol |= PCCARD_VCC_5V;
|
|
|
|
}
|
|
|
|
if (0x800u & psr) {
|
|
|
|
vol |= PCCARD_VCC_3V;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return vol;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
STATIC int
|
2006-11-16 04:32:37 +03:00
|
|
|
cbbprint(void *aux, const char *pcic)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2006-10-12 05:30:41 +04:00
|
|
|
#if 0
|
|
|
|
struct cbslot_attach_args *cba = aux;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2006-10-12 05:30:41 +04:00
|
|
|
if (cba->cba_slot >= 0) {
|
|
|
|
aprint_normal(" slot %d", cba->cba_slot);
|
|
|
|
}
|
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
return UNCONF;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_cardenable(struct pccbb_softc *sc, int function)
|
|
|
|
* This function enables and disables the card
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_cardenable(struct pccbb_softc *sc, int function)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int32_t command =
|
|
|
|
pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
|
|
|
|
DPRINTF(("pccbb_cardenable:"));
|
|
|
|
switch (function) {
|
|
|
|
case CARDBUS_IO_ENABLE:
|
|
|
|
command |= PCI_COMMAND_IO_ENABLE;
|
|
|
|
break;
|
|
|
|
case CARDBUS_IO_DISABLE:
|
|
|
|
command &= ~PCI_COMMAND_IO_ENABLE;
|
|
|
|
break;
|
|
|
|
case CARDBUS_MEM_ENABLE:
|
|
|
|
command |= PCI_COMMAND_MEM_ENABLE;
|
|
|
|
break;
|
|
|
|
case CARDBUS_MEM_DISABLE:
|
|
|
|
command &= ~PCI_COMMAND_MEM_ENABLE;
|
|
|
|
break;
|
|
|
|
case CARDBUS_BM_ENABLE:
|
|
|
|
command |= PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
break;
|
|
|
|
case CARDBUS_BM_DISABLE:
|
|
|
|
command &= ~PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, command);
|
|
|
|
DPRINTF((" command reg 0x%x\n", command));
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#if !rbus
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_io_open(cardbus_chipset_tag_t ct, int win, uint32_t start, uint32_t end)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
int basereg;
|
|
|
|
int limitreg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if ((win < 0) || (win > 2)) {
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined DIAGNOSTIC
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("cardbus_io_open: window out of range %d\n", win);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-01-14 06:01:41 +03:00
|
|
|
basereg = win * 8 + PCI_CB_IOBASE0;
|
|
|
|
limitreg = win * 8 + PCI_CB_IOLIMIT0;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_io_open: 0x%x[0x%x] - 0x%x[0x%x]\n",
|
|
|
|
start, basereg, end, limitreg));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, start);
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, end);
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
2000-02-05 21:20:08 +03:00
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* int pccbb_io_close(cardbus_chipset_tag_t, int)
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_io_close(cardbus_chipset_tag_t ct, int win)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
int basereg;
|
|
|
|
int limitreg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if ((win < 0) || (win > 2)) {
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined DIAGNOSTIC
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("cardbus_io_close: window out of range %d\n", win);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-01-14 06:01:41 +03:00
|
|
|
basereg = win * 8 + PCI_CB_IOBASE0;
|
|
|
|
limitreg = win * 8 + PCI_CB_IOLIMIT0;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, 0);
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, 0);
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_mem_open(cardbus_chipset_tag_t ct, int win, uint32_t start, uint32_t end)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
int basereg;
|
|
|
|
int limitreg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if ((win < 0) || (win > 2)) {
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined DIAGNOSTIC
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("cardbus_mem_open: window out of range %d\n", win);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-01-14 06:01:41 +03:00
|
|
|
basereg = win * 8 + PCI_CB_MEMBASE0;
|
|
|
|
limitreg = win * 8 + PCI_CB_MEMLIMIT0;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, start);
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, end);
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_mem_close(cardbus_chipset_tag_t ct, int win)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
int basereg;
|
|
|
|
int limitreg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if ((win < 0) || (win > 2)) {
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined DIAGNOSTIC
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("cardbus_mem_close: window out of range %d\n", win);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-01-14 06:01:41 +03:00
|
|
|
basereg = win * 8 + PCI_CB_MEMBASE0;
|
|
|
|
limitreg = win * 8 + PCI_CB_MEMLIMIT0;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, basereg, 0);
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, limitreg, 0);
|
|
|
|
return 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2000-01-26 12:02:41 +03:00
|
|
|
/*
|
2000-02-23 10:28:54 +03:00
|
|
|
* static void *pccbb_cb_intr_establish(cardbus_chipset_tag_t ct,
|
|
|
|
* int level,
|
2005-02-04 05:10:35 +03:00
|
|
|
* int (* func)(void *),
|
2000-02-23 10:28:54 +03:00
|
|
|
* void *arg)
|
|
|
|
*
|
|
|
|
* This function registers an interrupt handler at the bridge, in
|
2000-03-14 04:29:30 +03:00
|
|
|
* order not to call the interrupt handlers of child devices when
|
|
|
|
* a card-deletion interrupt occurs.
|
2000-02-23 10:28:54 +03:00
|
|
|
*
|
2011-08-01 15:20:26 +04:00
|
|
|
* The argument level is not used.
|
2000-02-23 10:28:54 +03:00
|
|
|
*/
|
|
|
|
static void *
|
2011-08-01 15:20:26 +04:00
|
|
|
pccbb_cb_intr_establish(cardbus_chipset_tag_t ct, int level,
|
|
|
|
int (*func)(void *), void *arg)
|
2000-02-23 10:28:54 +03:00
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
|
2011-08-01 15:20:26 +04:00
|
|
|
return pccbb_intr_establish(sc, level, func, arg);
|
2000-02-23 10:28:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* static void *pccbb_cb_intr_disestablish(cardbus_chipset_tag_t ct,
|
|
|
|
* void *ih)
|
|
|
|
*
|
|
|
|
* This function removes an interrupt handler pointed by ih.
|
|
|
|
*/
|
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_cb_intr_disestablish(cardbus_chipset_tag_t ct, void *ih)
|
2000-02-23 10:28:54 +03:00
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
|
|
|
|
pccbb_intr_disestablish(sc, ih);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-06 22:06:59 +04:00
|
|
|
void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_intr_route(struct pccbb_softc *sc)
|
|
|
|
{
|
|
|
|
pcireg_t bcr, cbctrl;
|
|
|
|
|
|
|
|
/* initialize bridge intr routing */
|
2007-08-11 04:31:04 +04:00
|
|
|
bcr = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG);
|
2007-02-04 08:34:38 +03:00
|
|
|
bcr &= ~CB_BCR_INTR_IREQ_ENABLE;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG, bcr);
|
2007-02-04 08:34:38 +03:00
|
|
|
|
|
|
|
switch (sc->sc_chipset) {
|
|
|
|
case CB_TI113X:
|
|
|
|
cbctrl = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CBCTRL);
|
|
|
|
/* functional intr enabled */
|
|
|
|
cbctrl |= PCI113X_CBCTRL_PCI_INTR;
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_CBCTRL, cbctrl);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2001-07-06 22:06:59 +04:00
|
|
|
}
|
|
|
|
|
2000-02-23 10:28:54 +03:00
|
|
|
/*
|
|
|
|
* static void *pccbb_intr_establish(struct pccbb_softc *sc,
|
2000-01-26 12:02:41 +03:00
|
|
|
* int irq,
|
|
|
|
* int level,
|
2005-02-04 05:10:35 +03:00
|
|
|
* int (* func)(void *),
|
2000-01-26 12:02:41 +03:00
|
|
|
* void *arg)
|
|
|
|
*
|
|
|
|
* This function registers an interrupt handler at the bridge, in
|
2000-03-14 04:29:30 +03:00
|
|
|
* order not to call the interrupt handlers of child devices when
|
|
|
|
* a card-deletion interrupt occurs.
|
2000-01-26 12:02:41 +03:00
|
|
|
*
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static void *
|
2011-08-01 15:20:26 +04:00
|
|
|
pccbb_intr_establish(struct pccbb_softc *sc, int level,
|
|
|
|
int (*func)(void *), void *arg)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_intrhand_list *pil, *newpil;
|
|
|
|
|
2002-10-01 18:30:54 +04:00
|
|
|
DPRINTF(("pccbb_intr_establish start. %p\n", LIST_FIRST(&sc->sc_pil)));
|
2000-02-23 10:28:54 +03:00
|
|
|
|
2002-10-01 13:09:16 +04:00
|
|
|
if (LIST_EMPTY(&sc->sc_pil)) {
|
|
|
|
pccbb_intr_route(sc);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2000-01-26 12:02:41 +03:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-03-14 04:29:30 +03:00
|
|
|
* Allocate a room for interrupt handler structure.
|
2000-02-05 21:20:08 +03:00
|
|
|
*/
|
|
|
|
if (NULL == (newpil =
|
|
|
|
(struct pccbb_intrhand_list *)malloc(sizeof(struct
|
|
|
|
pccbb_intrhand_list), M_DEVBUF, M_WAITOK))) {
|
|
|
|
return NULL;
|
2000-01-26 12:02:41 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
newpil->pil_func = func;
|
|
|
|
newpil->pil_arg = arg;
|
2006-12-21 18:55:21 +03:00
|
|
|
newpil->pil_icookie = makeiplcookie(level);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2002-10-01 13:09:16 +04:00
|
|
|
if (LIST_EMPTY(&sc->sc_pil)) {
|
|
|
|
LIST_INSERT_HEAD(&sc->sc_pil, newpil, pil_next);
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
2002-10-01 13:09:16 +04:00
|
|
|
for (pil = LIST_FIRST(&sc->sc_pil);
|
|
|
|
LIST_NEXT(pil, pil_next) != NULL;
|
|
|
|
pil = LIST_NEXT(pil, pil_next));
|
|
|
|
LIST_INSERT_AFTER(pil, newpil, pil_next);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2002-10-01 18:30:54 +04:00
|
|
|
DPRINTF(("pccbb_intr_establish add pil. %p\n",
|
|
|
|
LIST_FIRST(&sc->sc_pil)));
|
2000-02-23 10:28:54 +03:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return newpil;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-01-26 12:02:41 +03:00
|
|
|
/*
|
2000-02-23 10:28:54 +03:00
|
|
|
* static void *pccbb_intr_disestablish(struct pccbb_softc *sc,
|
2000-01-26 12:02:41 +03:00
|
|
|
* void *ih)
|
|
|
|
*
|
2002-10-01 13:09:16 +04:00
|
|
|
* This function removes an interrupt handler pointed by ih. ih
|
|
|
|
* should be the value returned by cardbus_intr_establish() or
|
|
|
|
* NULL.
|
|
|
|
*
|
|
|
|
* When ih is NULL, this function will do nothing.
|
2000-01-26 12:02:41 +03:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_intr_disestablish(struct pccbb_softc *sc, void *ih)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2002-10-01 13:09:16 +04:00
|
|
|
struct pccbb_intrhand_list *pil;
|
2000-11-28 12:11:36 +03:00
|
|
|
pcireg_t reg;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2002-10-01 18:30:54 +04:00
|
|
|
DPRINTF(("pccbb_intr_disestablish start. %p\n",
|
|
|
|
LIST_FIRST(&sc->sc_pil)));
|
2000-02-23 10:28:54 +03:00
|
|
|
|
2002-10-01 13:09:16 +04:00
|
|
|
if (ih == NULL) {
|
|
|
|
/* intr handler is not set */
|
|
|
|
DPRINTF(("pccbb_intr_disestablish: no ih\n"));
|
|
|
|
return;
|
|
|
|
}
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2002-10-01 13:09:16 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2007-12-20 23:48:24 +03:00
|
|
|
LIST_FOREACH(pil, &sc->sc_pil, pil_next) {
|
2002-10-19 22:01:59 +04:00
|
|
|
DPRINTF(("pccbb_intr_disestablish: pil %p\n", pil));
|
2000-02-05 21:20:08 +03:00
|
|
|
if (pil == ih) {
|
2000-02-23 10:28:54 +03:00
|
|
|
DPRINTF(("pccbb_intr_disestablish frees one pil\n"));
|
2000-02-05 21:20:08 +03:00
|
|
|
break;
|
|
|
|
}
|
2000-01-26 12:02:41 +03:00
|
|
|
}
|
2002-10-01 13:09:16 +04:00
|
|
|
if (pil == NULL) {
|
|
|
|
panic("pccbb_intr_disestablish: %s cannot find pil %p",
|
2008-06-25 15:42:32 +04:00
|
|
|
device_xname(sc->sc_dev), ih);
|
2002-10-01 13:09:16 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
pil = (struct pccbb_intrhand_list *)ih;
|
|
|
|
LIST_REMOVE(pil, pil_next);
|
|
|
|
free(pil, M_DEVBUF);
|
|
|
|
DPRINTF(("pccbb_intr_disestablish frees one pil\n"));
|
2000-01-26 12:02:41 +03:00
|
|
|
|
2002-10-01 13:09:16 +04:00
|
|
|
if (LIST_EMPTY(&sc->sc_pil)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
/* No interrupt handlers */
|
|
|
|
|
2000-02-23 10:28:54 +03:00
|
|
|
DPRINTF(("pccbb_intr_disestablish: no interrupt handler\n"));
|
|
|
|
|
2000-11-28 12:11:36 +03:00
|
|
|
/* stop routing PCI intr */
|
2007-08-11 04:31:04 +04:00
|
|
|
reg = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG);
|
2000-11-28 12:11:36 +03:00
|
|
|
reg |= CB_BCR_INTR_IREQ_ENABLE;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_BRIDGE_CONTROL_REG, reg);
|
2000-11-28 12:11:36 +03:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
switch (sc->sc_chipset) {
|
|
|
|
case CB_TI113X:
|
2000-11-28 12:11:36 +03:00
|
|
|
reg = pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_CBCTRL);
|
|
|
|
/* functional intr disabled */
|
|
|
|
reg &= ~PCI113X_CBCTRL_PCI_INTR;
|
|
|
|
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_CBCTRL, reg);
|
|
|
|
break;
|
2000-02-05 21:20:08 +03:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2000-01-26 12:02:41 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined SHOW_REGS
|
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
cb_show_regs(pci_chipset_tag_t pc, pcitag_t tag, bus_space_tag_t memt,
|
|
|
|
bus_space_handle_t memh)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
int i;
|
|
|
|
printf("PCI config regs:");
|
|
|
|
for (i = 0; i < 0x50; i += 4) {
|
2007-02-04 08:34:38 +03:00
|
|
|
if (i % 16 == 0)
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("\n 0x%02x:", i);
|
|
|
|
printf(" %08x", pci_conf_read(pc, tag, i));
|
|
|
|
}
|
|
|
|
for (i = 0x80; i < 0xb0; i += 4) {
|
2007-02-04 08:34:38 +03:00
|
|
|
if (i % 16 == 0)
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("\n 0x%02x:", i);
|
|
|
|
printf(" %08x", pci_conf_read(pc, tag, i));
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (memh == 0) {
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
printf("\nsocket regs:");
|
2007-02-04 08:34:38 +03:00
|
|
|
for (i = 0; i <= 0x10; i += 0x04)
|
2000-02-05 21:20:08 +03:00
|
|
|
printf(" %08x", bus_space_read_4(memt, memh, i));
|
|
|
|
printf("\nExCA regs:");
|
2007-02-04 08:34:38 +03:00
|
|
|
for (i = 0; i < 0x08; ++i)
|
2000-02-05 21:20:08 +03:00
|
|
|
printf(" %02x", bus_space_read_1(memt, memh, 0x800 + i));
|
|
|
|
printf("\n");
|
|
|
|
return;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
2010-02-25 02:38:40 +03:00
|
|
|
* static pcitag_t pccbb_make_tag(cardbus_chipset_tag_t cc,
|
2005-09-09 18:50:58 +04:00
|
|
|
* int busno, int function)
|
1999-10-19 13:29:46 +04:00
|
|
|
* This is the function to make a tag to access config space of
|
|
|
|
* a CardBus Card. It works same as pci_conf_read.
|
|
|
|
*/
|
2010-02-25 02:38:40 +03:00
|
|
|
static pcitag_t
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_make_tag(cardbus_chipset_tag_t cc, int busno, int function)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)cc;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-09-09 18:50:58 +04:00
|
|
|
return pci_make_tag(sc->sc_pc, busno, 0, function);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
2007-02-04 08:34:38 +03:00
|
|
|
* pccbb_conf_read
|
|
|
|
*
|
|
|
|
* This is the function to read the config space of a CardBus card.
|
|
|
|
* It works the same as pci_conf_read(9).
|
1999-10-19 13:29:46 +04:00
|
|
|
*/
|
2010-02-25 02:38:40 +03:00
|
|
|
static pcireg_t
|
|
|
|
pccbb_conf_read(cardbus_chipset_tag_t cc, pcitag_t tag, int offset)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)cc;
|
2008-07-03 17:37:34 +04:00
|
|
|
pcitag_t brtag = sc->sc_tag;
|
2010-02-25 02:38:40 +03:00
|
|
|
pcireg_t reg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-07-03 17:37:34 +04:00
|
|
|
/*
|
|
|
|
* clear cardbus master abort status; it is OK to write without
|
|
|
|
* reading before because all bits are r/o or w1tc
|
|
|
|
*/
|
|
|
|
pci_conf_write(sc->sc_pc, brtag, PCI_CBB_SECSTATUS,
|
|
|
|
CBB_SECSTATUS_CBMABORT);
|
|
|
|
reg = pci_conf_read(sc->sc_pc, tag, offset);
|
|
|
|
/* check cardbus master abort status */
|
|
|
|
if (pci_conf_read(sc->sc_pc, brtag, PCI_CBB_SECSTATUS)
|
|
|
|
& CBB_SECSTATUS_CBMABORT)
|
|
|
|
return (0xffffffff);
|
|
|
|
return reg;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
2007-02-04 08:34:38 +03:00
|
|
|
* pccbb_conf_write
|
|
|
|
*
|
|
|
|
* This is the function to write the config space of a CardBus
|
|
|
|
* card. It works the same as pci_conf_write(9).
|
1999-10-19 13:29:46 +04:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static void
|
2010-02-25 02:38:40 +03:00
|
|
|
pccbb_conf_write(cardbus_chipset_tag_t cc, pcitag_t tag, int reg, pcireg_t val)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)cc;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_conf_write(sc->sc_pc, tag, reg, val);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
STATIC int
|
|
|
|
pccbb_new_pcmcia_io_alloc(pcmcia_chipset_handle_t pch,
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t start, bus_size_t size, bus_size_t align, bus_addr_t mask,
|
|
|
|
int speed, int flags,
|
|
|
|
bus_space_handle_t * iohp)
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_io_alloc(pcmcia_chipset_handle_t pch,
|
|
|
|
* bus_addr_t start, bus_size_t size,
|
|
|
|
* bus_size_t align,
|
|
|
|
* struct pcmcia_io_handle *pcihp
|
|
|
|
*
|
|
|
|
* This function only allocates I/O region for pccard. This function
|
2000-03-14 04:29:30 +03:00
|
|
|
* never maps the allocated region to pccard I/O area.
|
1999-10-19 13:29:46 +04:00
|
|
|
*
|
|
|
|
* XXX: The interface of this function is not very good, I believe.
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start,
|
|
|
|
bus_size_t size, bus_size_t align, struct pcmcia_io_handle *pcihp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t ioaddr;
|
|
|
|
int flags = 0;
|
|
|
|
bus_space_tag_t iot;
|
|
|
|
bus_space_handle_t ioh;
|
2001-02-09 13:41:50 +03:00
|
|
|
bus_addr_t mask;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_tag_t rb;
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
if (align == 0) {
|
|
|
|
align = size; /* XXX: funny??? */
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2001-02-09 13:41:50 +03:00
|
|
|
if (start != 0) {
|
|
|
|
/* XXX: assume all card decode lower 10 bits by its hardware */
|
|
|
|
mask = 0x3ff;
|
2002-03-05 14:56:33 +03:00
|
|
|
/* enforce to use only masked address */
|
|
|
|
start &= mask;
|
2001-02-09 13:41:50 +03:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* calculate mask:
|
|
|
|
* 1. get the most significant bit of size (call it msb).
|
|
|
|
* 2. compare msb with the value of size.
|
|
|
|
* 3. if size is larger, shift msb left once.
|
|
|
|
* 4. obtain mask value to decrement msb.
|
|
|
|
*/
|
|
|
|
bus_size_t size_tmp = size;
|
|
|
|
int shifts = 0;
|
|
|
|
|
|
|
|
mask = 1;
|
|
|
|
while (size_tmp) {
|
|
|
|
++shifts;
|
|
|
|
size_tmp >>= 1;
|
|
|
|
}
|
|
|
|
mask = (1 << shifts);
|
|
|
|
if (mask < size) {
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
|
|
|
--mask;
|
|
|
|
}
|
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* Allocate some arbitrary I/O space.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
iot = sc->sc_iot;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if rbus
|
2008-06-27 00:57:10 +04:00
|
|
|
rb = sc->sc_rbus_iot;
|
2001-02-09 13:41:50 +03:00
|
|
|
if (rbus_space_alloc(rb, start, size, mask, align, 0, &ioaddr, &ioh)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_io_alloc alloc port 0x%lx+0x%lx\n",
|
2002-10-01 18:30:54 +04:00
|
|
|
(u_long) ioaddr, (u_long) size));
|
2000-02-05 21:20:08 +03:00
|
|
|
#else
|
|
|
|
if (start) {
|
|
|
|
ioaddr = start;
|
|
|
|
if (bus_space_map(iot, start, size, 0, &ioh)) {
|
|
|
|
return 1;
|
|
|
|
}
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_io_alloc map port 0x%lx+0x%lx\n",
|
2000-02-05 21:20:08 +03:00
|
|
|
(u_long) ioaddr, (u_long) size));
|
|
|
|
} else {
|
|
|
|
flags |= PCMCIA_IO_ALLOCATED;
|
|
|
|
if (bus_space_alloc(iot, 0x700 /* ph->sc->sc_iobase */ ,
|
|
|
|
0x800, /* ph->sc->sc_iobase + ph->sc->sc_iosize */
|
|
|
|
size, align, 0, 0, &ioaddr, &ioh)) {
|
|
|
|
/* No room be able to be get. */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
DPRINTF(("pccbb_pcmmcia_io_alloc alloc port 0x%lx+0x%lx\n",
|
|
|
|
(u_long) ioaddr, (u_long) size));
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pcihp->iot = iot;
|
|
|
|
pcihp->ioh = ioh;
|
|
|
|
pcihp->addr = ioaddr;
|
|
|
|
pcihp->size = size;
|
|
|
|
pcihp->flags = flags;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_io_free(pcmcia_chipset_handle_t pch,
|
|
|
|
* struct pcmcia_io_handle *pcihp)
|
|
|
|
*
|
|
|
|
* This function only frees I/O region for pccard.
|
|
|
|
*
|
|
|
|
* XXX: The interface of this function is not very good, I believe.
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_io_free(pcmcia_chipset_handle_t pch,
|
|
|
|
struct pcmcia_io_handle *pcihp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if !rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_tag_t iot = pcihp->iot;
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_handle_t ioh = pcihp->ioh;
|
|
|
|
bus_size_t size = pcihp->size;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_tag_t rb = sc->sc_rbus_iot;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_space_free(rb, ioh, size, NULL);
|
1999-10-15 10:07:17 +04:00
|
|
|
#else
|
2000-02-05 21:20:08 +03:00
|
|
|
if (pcihp->flags & PCMCIA_IO_ALLOCATED)
|
|
|
|
bus_space_free(iot, ioh, size);
|
|
|
|
else
|
|
|
|
bus_space_unmap(iot, ioh, size);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_io_map(pcmcia_chipset_handle_t pch, int width,
|
|
|
|
* bus_addr_t offset, bus_size_t size,
|
|
|
|
* struct pcmcia_io_handle *pcihp,
|
|
|
|
* int *windowp)
|
|
|
|
*
|
|
|
|
* This function maps the allocated I/O region to pccard. This function
|
|
|
|
* never allocates any I/O region for pccard I/O area. I don't
|
|
|
|
* understand why the original authors of pcmciabus separated alloc and
|
|
|
|
* map. I believe the two must be unite.
|
|
|
|
*
|
|
|
|
* XXX: no wait timing control?
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset,
|
|
|
|
bus_size_t size, struct pcmcia_io_handle *pcihp, int *windowp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t ioaddr = pcihp->addr + offset;
|
|
|
|
int i, win;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if defined CBB_DEBUG
|
2005-06-01 13:10:57 +04:00
|
|
|
static const char *width_names[] = { "dynamic", "io8", "io16" };
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* Sanity check I/O handle. */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-04-19 22:24:26 +04:00
|
|
|
if (!bus_space_is_equal(sc->sc_iot, pcihp->iot)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
panic("pccbb_pcmcia_io_map iot is bogus");
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* XXX Sanity check offset/size. */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
win = -1;
|
|
|
|
for (i = 0; i < PCIC_IO_WINS; i++) {
|
|
|
|
if ((ph->ioalloc & (1 << i)) == 0) {
|
|
|
|
win = i;
|
|
|
|
ph->ioalloc |= (1 << i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (win == -1) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
*windowp = win;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* XXX this is pretty gross */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_io_map window %d %s port %lx+%lx\n",
|
|
|
|
win, width_names[width], (u_long) ioaddr, (u_long) size));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* XXX wtf is this doing here? */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if 0
|
2000-02-05 21:20:08 +03:00
|
|
|
printf(" port 0x%lx", (u_long) ioaddr);
|
|
|
|
if (size > 1) {
|
|
|
|
printf("-0x%lx", (u_long) ioaddr + (u_long) size - 1);
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
ph->io[win].addr = ioaddr;
|
|
|
|
ph->io[win].size = size;
|
|
|
|
ph->io[win].width = width;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* actual dirty register-value changing in the function below. */
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_io_map(sc, win);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_do_io_map(struct pcic_handle *h, int win)
|
|
|
|
*
|
|
|
|
* This function changes register-value to map I/O region for pccard.
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
static void
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_io_map(struct pccbb_softc *sc, int win)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
static u_int8_t pcic_iowidth[3] = {
|
|
|
|
PCIC_IOCTL_IO0_IOCS16SRC_CARD,
|
|
|
|
PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE |
|
|
|
|
PCIC_IOCTL_IO0_DATASIZE_8BIT,
|
|
|
|
PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE |
|
|
|
|
PCIC_IOCTL_IO0_DATASIZE_16BIT,
|
|
|
|
};
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#define PCIC_SIA_START_LOW 0
|
|
|
|
#define PCIC_SIA_START_HIGH 1
|
|
|
|
#define PCIC_SIA_STOP_LOW 2
|
|
|
|
#define PCIC_SIA_STOP_HIGH 3
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
int regbase_win = 0x8 + win * 0x04;
|
|
|
|
u_int8_t ioctl, enable;
|
2008-06-26 22:05:48 +04:00
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_do_io_map win %d addr 0x%lx size 0x%lx "
|
|
|
|
"width %d\n", win, (unsigned long)ph->io[win].addr,
|
|
|
|
(unsigned long)ph->io[win].size, ph->io[win].width * 8));
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SIA_START_LOW,
|
2000-02-05 21:20:08 +03:00
|
|
|
ph->io[win].addr & 0xff);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SIA_START_HIGH,
|
2000-02-05 21:20:08 +03:00
|
|
|
(ph->io[win].addr >> 8) & 0xff);
|
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SIA_STOP_LOW,
|
2000-02-05 21:20:08 +03:00
|
|
|
(ph->io[win].addr + ph->io[win].size - 1) & 0xff);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SIA_STOP_HIGH,
|
2000-02-05 21:20:08 +03:00
|
|
|
((ph->io[win].addr + ph->io[win].size - 1) >> 8) & 0xff);
|
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
ioctl = Pcic_read(sc, PCIC_IOCTL);
|
|
|
|
enable = Pcic_read(sc, PCIC_ADDRWIN_ENABLE);
|
2000-02-05 21:20:08 +03:00
|
|
|
switch (win) {
|
|
|
|
case 0:
|
|
|
|
ioctl &= ~(PCIC_IOCTL_IO0_WAITSTATE | PCIC_IOCTL_IO0_ZEROWAIT |
|
|
|
|
PCIC_IOCTL_IO0_IOCS16SRC_MASK |
|
|
|
|
PCIC_IOCTL_IO0_DATASIZE_MASK);
|
|
|
|
ioctl |= pcic_iowidth[ph->io[win].width];
|
|
|
|
enable |= PCIC_ADDRWIN_ENABLE_IO0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
ioctl &= ~(PCIC_IOCTL_IO1_WAITSTATE | PCIC_IOCTL_IO1_ZEROWAIT |
|
|
|
|
PCIC_IOCTL_IO1_IOCS16SRC_MASK |
|
|
|
|
PCIC_IOCTL_IO1_DATASIZE_MASK);
|
|
|
|
ioctl |= (pcic_iowidth[ph->io[win].width] << 4);
|
|
|
|
enable |= PCIC_ADDRWIN_ENABLE_IO1;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_IOCTL, ioctl);
|
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, enable);
|
2006-07-09 03:02:55 +04:00
|
|
|
#if defined(CBB_DEBUG)
|
2000-02-05 21:20:08 +03:00
|
|
|
{
|
|
|
|
u_int8_t start_low =
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_read(sc, regbase_win + PCIC_SIA_START_LOW);
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int8_t start_high =
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_read(sc, regbase_win + PCIC_SIA_START_HIGH);
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int8_t stop_low =
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_read(sc, regbase_win + PCIC_SIA_STOP_LOW);
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int8_t stop_high =
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_read(sc, regbase_win + PCIC_SIA_STOP_HIGH);
|
2006-07-09 03:02:55 +04:00
|
|
|
printf("pccbb_pcmcia_do_io_map start %02x %02x, "
|
|
|
|
"stop %02x %02x, ioctl %02x enable %02x\n",
|
2000-02-05 21:20:08 +03:00
|
|
|
start_low, start_high, stop_low, stop_high, ioctl, enable);
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_io_unmap(pcmcia_chipset_handle_t *h, int win)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function unmaps I/O region. No return value.
|
1999-10-19 13:29:46 +04:00
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_io_unmap(pcmcia_chipset_handle_t pch, int win)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
int reg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (win >= PCIC_IO_WINS || win < 0) {
|
|
|
|
panic("pccbb_pcmcia_io_unmap: window out of range");
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
reg = Pcic_read(sc, PCIC_ADDRWIN_ENABLE);
|
2000-02-05 21:20:08 +03:00
|
|
|
switch (win) {
|
|
|
|
case 0:
|
|
|
|
reg &= ~PCIC_ADDRWIN_ENABLE_IO0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
reg &= ~PCIC_ADDRWIN_ENABLE_IO1;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, reg);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
ph->ioalloc &= ~(1 << win);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
2003-10-23 04:04:03 +04:00
|
|
|
static int
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_wait_ready(struct pccbb_softc *sc)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2004-08-12 11:15:49 +04:00
|
|
|
u_int8_t stat;
|
2000-02-05 21:20:08 +03:00
|
|
|
int i;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-12 11:15:49 +04:00
|
|
|
/* wait an initial 10ms for quick cards */
|
2008-06-27 00:57:10 +04:00
|
|
|
stat = Pcic_read(sc, PCIC_IF_STATUS);
|
2004-08-12 11:15:49 +04:00
|
|
|
if (stat & PCIC_IF_STATUS_READY)
|
|
|
|
return (0);
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(sc, 10, "pccwr0");
|
2004-08-12 11:15:49 +04:00
|
|
|
for (i = 0; i < 50; i++) {
|
2008-06-27 00:57:10 +04:00
|
|
|
stat = Pcic_read(sc, PCIC_IF_STATUS);
|
2003-10-23 04:04:03 +04:00
|
|
|
if (stat & PCIC_IF_STATUS_READY)
|
2004-08-12 11:15:49 +04:00
|
|
|
return (0);
|
2003-10-23 04:04:03 +04:00
|
|
|
if ((stat & PCIC_IF_STATUS_CARDDETECT_MASK) !=
|
|
|
|
PCIC_IF_STATUS_CARDDETECT_PRESENT)
|
2004-08-12 11:15:49 +04:00
|
|
|
return (ENXIO);
|
|
|
|
/* wait .1s (100ms) each iteration now */
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(sc, 100, "pccwr1");
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-12 11:15:49 +04:00
|
|
|
printf("pccbb_pcmcia_wait_ready: ready never happened, status=%02x\n", stat);
|
|
|
|
return (EWOULDBLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-02-04 08:34:38 +03:00
|
|
|
* Perform long (msec order) delay. timo is in milliseconds.
|
2004-08-12 11:15:49 +04:00
|
|
|
*/
|
|
|
|
static void
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(struct pccbb_softc *sc, int timo, const char *wmesg)
|
2004-08-12 11:15:49 +04:00
|
|
|
{
|
1999-10-15 10:07:17 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2004-08-12 11:15:49 +04:00
|
|
|
if (timo <= 0)
|
|
|
|
panic("pccbb_pcmcia_delay: called with timeout %d", timo);
|
|
|
|
if (!curlwp)
|
|
|
|
panic("pccbb_pcmcia_delay: called in interrupt context");
|
|
|
|
#endif
|
2008-06-26 21:22:23 +04:00
|
|
|
DPRINTF(("pccbb_pcmcia_delay: \"%s\", sleep %d ms\n", wmesg, timo));
|
2009-07-24 01:22:25 +04:00
|
|
|
kpause(wmesg, false, max(mstohz(timo), 1), NULL);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_socket_enable(pcmcia_chipset_handle_t pch)
|
|
|
|
*
|
|
|
|
* This function enables the card. All information is stored in
|
|
|
|
* the first argument, pcmcia_chipset_handle_t.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_socket_enable(pcmcia_chipset_handle_t pch)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
pcireg_t spsr;
|
|
|
|
int voltage;
|
2004-08-12 11:15:49 +04:00
|
|
|
int win;
|
|
|
|
u_int8_t power, intr;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
int reg;
|
|
|
|
#endif
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* this bit is mostly stolen from pcic_attach_card */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_socket_enable: "));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* get card Vcc info */
|
|
|
|
spsr =
|
|
|
|
bus_space_read_4(sc->sc_base_memt, sc->sc_base_memh,
|
|
|
|
CB_SOCKET_STAT);
|
|
|
|
if (spsr & CB_SOCKET_STAT_5VCARD) {
|
|
|
|
DPRINTF(("5V card\n"));
|
|
|
|
voltage = CARDBUS_VCC_5V | CARDBUS_VPP_VCC;
|
|
|
|
} else if (spsr & CB_SOCKET_STAT_3VCARD) {
|
|
|
|
DPRINTF(("3V card\n"));
|
|
|
|
voltage = CARDBUS_VCC_3V | CARDBUS_VPP_VCC;
|
|
|
|
} else {
|
2006-07-09 03:02:55 +04:00
|
|
|
DPRINTF(("?V card, 0x%x\n", spsr)); /* XXX */
|
2000-02-05 21:20:08 +03:00
|
|
|
return;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/* disable interrupts; assert RESET */
|
2008-06-27 00:57:10 +04:00
|
|
|
intr = Pcic_read(sc, PCIC_INTR);
|
2004-08-16 19:46:37 +04:00
|
|
|
intr &= PCIC_INTR_ENABLE;
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_INTR, intr);
|
2004-08-12 11:15:49 +04:00
|
|
|
|
|
|
|
/* zero out the address windows */
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, 0);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-12 11:15:49 +04:00
|
|
|
/* power down the socket to reset it, clear the card reset pin */
|
|
|
|
pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/* power off; assert output enable bit */
|
|
|
|
power = PCIC_PWRCTL_OE;
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_PWRCTL, power);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-16 00:19:14 +04:00
|
|
|
/* power up the socket */
|
2004-08-12 11:15:49 +04:00
|
|
|
if (pccbb_power(sc, voltage) == 0)
|
|
|
|
return;
|
2003-12-10 05:55:48 +03:00
|
|
|
|
2005-01-16 11:56:29 +03:00
|
|
|
/*
|
|
|
|
* Table 4-18 and figure 4-6 of the PC Card specifiction say:
|
|
|
|
* Vcc Rising Time (Tpr) = 100ms (handled in pccbb_power() above)
|
|
|
|
* RESET Width (Th (Hi-z RESET)) = 1ms
|
|
|
|
* RESET Width (Tw (RESET)) = 10us
|
2009-08-07 16:04:43 +04:00
|
|
|
*
|
2006-07-09 00:20:27 +04:00
|
|
|
* some machines require some more time to be settled
|
|
|
|
* for example old toshiba topic bridges!
|
|
|
|
* (100ms is added here).
|
2009-08-07 16:04:43 +04:00
|
|
|
*/
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(sc, 200 + 1, "pccen1");
|
2005-01-16 11:56:29 +03:00
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/* negate RESET */
|
2000-02-05 21:20:08 +03:00
|
|
|
intr |= PCIC_INTR_RESET;
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_INTR, intr);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/*
|
|
|
|
* RESET Setup Time (Tsu (RESET)) = 20ms
|
|
|
|
*/
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(sc, 20, "pccen2");
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2004-08-12 11:15:49 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2008-06-27 00:57:10 +04:00
|
|
|
reg = Pcic_read(sc, PCIC_IF_STATUS);
|
2004-08-12 11:15:49 +04:00
|
|
|
if ((reg & PCIC_IF_STATUS_POWERACTIVE) == 0)
|
|
|
|
printf("pccbb_pcmcia_socket_enable: no power, status=%x\n", reg);
|
2001-01-30 10:23:14 +03:00
|
|
|
#endif
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* wait for the chip to finish initializing */
|
2008-06-26 22:05:48 +04:00
|
|
|
if (pccbb_pcmcia_wait_ready(sc)) {
|
2006-07-09 03:02:55 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
printf("pccbb_pcmcia_socket_enable: never became ready\n");
|
|
|
|
#endif
|
2004-08-12 11:15:49 +04:00
|
|
|
/* XXX return a failure status?? */
|
2003-10-23 04:04:03 +04:00
|
|
|
pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_PWRCTL, 0);
|
2003-10-23 04:04:03 +04:00
|
|
|
return;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* reinstall all the memory and io mappings */
|
2004-08-12 11:15:49 +04:00
|
|
|
for (win = 0; win < PCIC_MEM_WINS; ++win)
|
|
|
|
if (ph->memalloc & (1 << win))
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_mem_map(sc, win);
|
2004-08-12 11:15:49 +04:00
|
|
|
for (win = 0; win < PCIC_IO_WINS; ++win)
|
|
|
|
if (ph->ioalloc & (1 << win))
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_io_map(sc, win);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_socket_disable(pcmcia_chipset_handle_t *ph)
|
|
|
|
*
|
|
|
|
* This function disables the card. All information is stored in
|
|
|
|
* the first argument, pcmcia_chipset_handle_t.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_socket_disable(pcmcia_chipset_handle_t pch)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
2004-08-12 11:15:49 +04:00
|
|
|
u_int8_t intr;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
|
|
|
DPRINTF(("pccbb_pcmcia_socket_disable\n"));
|
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/* disable interrupts; assert RESET */
|
2008-06-27 00:57:10 +04:00
|
|
|
intr = Pcic_read(sc, PCIC_INTR);
|
2004-08-16 19:46:37 +04:00
|
|
|
intr &= PCIC_INTR_ENABLE;
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_INTR, intr);
|
2004-08-11 05:04:40 +04:00
|
|
|
|
|
|
|
/* zero out the address windows */
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, 0);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2004-08-12 11:15:49 +04:00
|
|
|
/* power down the socket to reset it, clear the card reset pin */
|
|
|
|
pccbb_power(sc, CARDBUS_VCC_0V | CARDBUS_VPP_0V);
|
2004-08-07 01:39:47 +04:00
|
|
|
|
2004-08-16 19:40:35 +04:00
|
|
|
/* disable socket: negate output enable bit and power off */
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_PWRCTL, 0);
|
2004-08-16 19:40:35 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Vcc Falling Time (Tpf) = 300ms
|
|
|
|
*/
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_delay(sc, 300, "pccwr1");
|
2004-08-11 04:18:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_socket_settype(pcmcia_chipset_handle_t pch, int type)
|
2004-08-11 04:18:18 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
2004-08-11 04:18:18 +04:00
|
|
|
u_int8_t intr;
|
|
|
|
|
|
|
|
/* set the card type */
|
2004-08-07 01:39:47 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
intr = Pcic_read(sc, PCIC_INTR);
|
2004-08-11 05:04:40 +04:00
|
|
|
intr &= ~(PCIC_INTR_IRQ_MASK | PCIC_INTR_CARDTYPE_MASK);
|
2004-08-11 04:18:18 +04:00
|
|
|
if (type == PCMCIA_IFTYPE_IO)
|
|
|
|
intr |= PCIC_INTR_CARDTYPE_IO;
|
|
|
|
else
|
|
|
|
intr |= PCIC_INTR_CARDTYPE_MEM;
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_INTR, intr);
|
2004-08-11 04:18:18 +04:00
|
|
|
|
2008-06-26 21:22:23 +04:00
|
|
|
DPRINTF(("%s: pccbb_pcmcia_socket_settype type %s %02x\n",
|
2008-06-27 00:57:10 +04:00
|
|
|
device_xname(sc->sc_dev),
|
2008-06-26 21:22:23 +04:00
|
|
|
((type == PCMCIA_IFTYPE_IO) ? "io" : "mem"), intr));
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
1999-10-15 10:07:17 +04:00
|
|
|
* STATIC int pccbb_pcmcia_card_detect(pcmcia_chipset_handle_t *ph)
|
|
|
|
*
|
|
|
|
* This function detects whether a card is in the slot or not.
|
|
|
|
* If a card is inserted, return 1. Otherwise, return 0.
|
1999-10-19 13:29:46 +04:00
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_card_detect(pcmcia_chipset_handle_t pch)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_card_detect\n"));
|
|
|
|
return pccbb_detect_card(sc) == 1 ? 1 : 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
STATIC int
|
|
|
|
pccbb_new_pcmcia_mem_alloc(pcmcia_chipset_handle_t pch,
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t start, bus_size_t size, bus_size_t align, int speed, int flags,
|
|
|
|
bus_space_tag_t * memtp bus_space_handle_t * memhp)
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_mem_alloc(pcmcia_chipset_handle_t pch,
|
|
|
|
* bus_size_t size,
|
|
|
|
* struct pcmcia_mem_handle *pcmhp)
|
|
|
|
*
|
|
|
|
* This function only allocates memory region for pccard. This
|
2000-03-14 04:29:30 +03:00
|
|
|
* function never maps the allocated region to pccard memory area.
|
1999-10-19 13:29:46 +04:00
|
|
|
*
|
|
|
|
* XXX: Why the argument of start address is not in?
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
|
|
|
|
struct pcmcia_mem_handle *pcmhp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_handle_t memh;
|
|
|
|
bus_addr_t addr;
|
|
|
|
bus_size_t sizepg;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_tag_t rb;
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2003-10-23 04:04:03 +04:00
|
|
|
/* Check that the card is still there. */
|
2008-06-27 00:57:10 +04:00
|
|
|
if ((Pcic_read(sc, PCIC_IF_STATUS) & PCIC_IF_STATUS_CARDDETECT_MASK) !=
|
2003-10-23 04:04:03 +04:00
|
|
|
PCIC_IF_STATUS_CARDDETECT_PRESENT)
|
|
|
|
return 1;
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* out of sc->memh, allocate as many pages as necessary */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* convert size to PCIC pages */
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* This is not enough; when the requested region is on the page
|
|
|
|
* boundaries, this may calculate wrong result.
|
|
|
|
*/
|
|
|
|
sizepg = (size + (PCIC_MEM_PAGESIZE - 1)) / PCIC_MEM_PAGESIZE;
|
1999-10-15 10:07:17 +04:00
|
|
|
#if 0
|
2000-02-05 21:20:08 +03:00
|
|
|
if (sizepg > PCIC_MAX_MEM_PAGES) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (!(sc->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32)) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
addr = 0; /* XXX gcc -Wuninitialized */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#if rbus
|
2000-02-05 21:20:08 +03:00
|
|
|
rb = sc->sc_rbus_memt;
|
|
|
|
if (rbus_space_alloc(rb, 0, sizepg * PCIC_MEM_PAGESIZE,
|
|
|
|
sizepg * PCIC_MEM_PAGESIZE - 1, PCIC_MEM_PAGESIZE, 0,
|
|
|
|
&addr, &memh)) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#else
|
2000-02-05 21:20:08 +03:00
|
|
|
if (bus_space_alloc(sc->sc_memt, sc->sc_mem_start, sc->sc_mem_end,
|
|
|
|
sizepg * PCIC_MEM_PAGESIZE, PCIC_MEM_PAGESIZE,
|
|
|
|
0, /* boundary */
|
|
|
|
0, /* flags */
|
|
|
|
&addr, &memh)) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_alloc_mem: addr 0x%lx size 0x%lx, "
|
|
|
|
"realsize 0x%lx\n", (unsigned long)addr, (unsigned long)size,
|
|
|
|
(unsigned long)sizepg * PCIC_MEM_PAGESIZE));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pcmhp->memt = sc->sc_memt;
|
|
|
|
pcmhp->memh = memh;
|
|
|
|
pcmhp->addr = addr;
|
|
|
|
pcmhp->size = size;
|
|
|
|
pcmhp->realsize = sizepg * PCIC_MEM_PAGESIZE;
|
|
|
|
/* What is mhandle? I feel it is very dirty and it must go trush. */
|
|
|
|
pcmhp->mhandle = 0;
|
|
|
|
/* No offset??? Funny. */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_mem_free(pcmcia_chipset_handle_t pch,
|
|
|
|
* struct pcmcia_mem_handle *pcmhp)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function release the memory space allocated by the function
|
1999-10-19 13:29:46 +04:00
|
|
|
* pccbb_pcmcia_mem_alloc().
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_mem_free(pcmcia_chipset_handle_t pch,
|
|
|
|
struct pcmcia_mem_handle *pcmhp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
|
|
|
#if rbus
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
rbus_space_free(sc->sc_rbus_memt, pcmhp->memh, pcmhp->realsize, NULL);
|
1999-10-15 10:07:17 +04:00
|
|
|
#else
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_space_free(pcmhp->memt, pcmhp->memh, pcmhp->realsize);
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_do_mem_map(struct pcic_handle *ph, int win)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function release the memory space allocated by the function
|
1999-10-19 13:29:46 +04:00
|
|
|
* pccbb_pcmcia_mem_alloc().
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC void
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_mem_map(struct pccbb_softc *sc, int win)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
int regbase_win;
|
|
|
|
bus_addr_t phys_addr;
|
|
|
|
bus_addr_t phys_end;
|
2008-06-26 22:05:48 +04:00
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#define PCIC_SMM_START_LOW 0
|
|
|
|
#define PCIC_SMM_START_HIGH 1
|
|
|
|
#define PCIC_SMM_STOP_LOW 2
|
|
|
|
#define PCIC_SMM_STOP_HIGH 3
|
|
|
|
#define PCIC_CMA_LOW 4
|
|
|
|
#define PCIC_CMA_HIGH 5
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
u_int8_t start_low, start_high = 0;
|
|
|
|
u_int8_t stop_low, stop_high;
|
|
|
|
u_int8_t off_low, off_high;
|
|
|
|
u_int8_t mem_window;
|
|
|
|
int reg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
int kind = ph->mem[win].kind & ~PCMCIA_WIDTH_MEM_MASK;
|
|
|
|
int mem8 =
|
2000-02-21 04:44:36 +03:00
|
|
|
(ph->mem[win].kind & PCMCIA_WIDTH_MEM_MASK) == PCMCIA_WIDTH_MEM8
|
|
|
|
|| (kind == PCMCIA_MEM_ATTR);
|
2000-01-13 12:01:17 +03:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
regbase_win = 0x10 + win * 0x08;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
phys_addr = ph->mem[win].addr;
|
|
|
|
phys_end = phys_addr + ph->mem[win].size;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_do_mem_map: start 0x%lx end 0x%lx off 0x%lx\n",
|
2003-12-19 22:00:00 +03:00
|
|
|
(unsigned long)phys_addr, (unsigned long)phys_end,
|
|
|
|
(unsigned long)ph->mem[win].offset));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
|
|
|
#define PCIC_MEMREG_LSB_SHIFT PCIC_SYSMEM_ADDRX_SHIFT
|
|
|
|
#define PCIC_MEMREG_MSB_SHIFT (PCIC_SYSMEM_ADDRX_SHIFT + 8)
|
|
|
|
#define PCIC_MEMREG_WIN_SHIFT (PCIC_SYSMEM_ADDRX_SHIFT + 12)
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* bit 19:12 */
|
|
|
|
start_low = (phys_addr >> PCIC_MEMREG_LSB_SHIFT) & 0xff;
|
|
|
|
/* bit 23:20 and bit 7 on */
|
|
|
|
start_high = ((phys_addr >> PCIC_MEMREG_MSB_SHIFT) & 0x0f)
|
|
|
|
|(mem8 ? 0 : PCIC_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT);
|
|
|
|
/* bit 31:24, for 32-bit address */
|
|
|
|
mem_window = (phys_addr >> PCIC_MEMREG_WIN_SHIFT) & 0xff;
|
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SMM_START_LOW, start_low);
|
|
|
|
Pcic_write(sc, regbase_win + PCIC_SMM_START_HIGH, start_high);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
if (sc->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) {
|
|
|
|
Pcic_write(sc, 0x40 + win, mem_window);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
stop_low = (phys_end >> PCIC_MEMREG_LSB_SHIFT) & 0xff;
|
|
|
|
stop_high = ((phys_end >> PCIC_MEMREG_MSB_SHIFT) & 0x0f)
|
|
|
|
| PCIC_SYSMEM_ADDRX_STOP_MSB_WAIT2; /* wait 2 cycles */
|
|
|
|
/* XXX Geee, WAIT2!! Crazy!! I must rewrite this routine. */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_SMM_STOP_LOW, stop_low);
|
|
|
|
Pcic_write(sc, regbase_win + PCIC_SMM_STOP_HIGH, stop_high);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
off_low = (ph->mem[win].offset >> PCIC_CARDMEM_ADDRX_SHIFT) & 0xff;
|
|
|
|
off_high = ((ph->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8))
|
|
|
|
& PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK)
|
|
|
|
| ((kind == PCMCIA_MEM_ATTR) ?
|
|
|
|
PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, regbase_win + PCIC_CMA_LOW, off_low);
|
|
|
|
Pcic_write(sc, regbase_win + PCIC_CMA_HIGH, off_high);
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
reg = Pcic_read(sc, PCIC_ADDRWIN_ENABLE);
|
2000-02-05 21:20:08 +03:00
|
|
|
reg |= ((1 << win) | PCIC_ADDRWIN_ENABLE_MEMCS16);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, reg);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2006-07-09 03:02:55 +04:00
|
|
|
#if defined(CBB_DEBUG)
|
2000-02-05 21:20:08 +03:00
|
|
|
{
|
|
|
|
int r1, r2, r3, r4, r5, r6, r7 = 0;
|
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
r1 = Pcic_read(sc, regbase_win + PCIC_SMM_START_LOW);
|
|
|
|
r2 = Pcic_read(sc, regbase_win + PCIC_SMM_START_HIGH);
|
|
|
|
r3 = Pcic_read(sc, regbase_win + PCIC_SMM_STOP_LOW);
|
|
|
|
r4 = Pcic_read(sc, regbase_win + PCIC_SMM_STOP_HIGH);
|
|
|
|
r5 = Pcic_read(sc, regbase_win + PCIC_CMA_LOW);
|
|
|
|
r6 = Pcic_read(sc, regbase_win + PCIC_CMA_HIGH);
|
|
|
|
if (sc->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) {
|
|
|
|
r7 = Pcic_read(sc, 0x40 + win);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
|
2006-07-09 03:02:55 +04:00
|
|
|
printf("pccbb_pcmcia_do_mem_map window %d: %02x%02x %02x%02x "
|
|
|
|
"%02x%02x", win, r1, r2, r3, r4, r5, r6);
|
2008-06-27 00:57:10 +04:00
|
|
|
if (sc->sc_pcmcia_flags & PCCBB_PCMCIA_MEM_32) {
|
2006-07-09 03:02:55 +04:00
|
|
|
printf(" %02x", r7);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2006-07-09 03:02:55 +04:00
|
|
|
printf("\n");
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_mem_map(pcmcia_chipset_handle_t pch, int kind,
|
|
|
|
* bus_addr_t card_addr, bus_size_t size,
|
|
|
|
* struct pcmcia_mem_handle *pcmhp,
|
|
|
|
* bus_addr_t *offsetp, int *windowp)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function maps memory space allocated by the function
|
1999-10-19 13:29:46 +04:00
|
|
|
* pccbb_pcmcia_mem_alloc().
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_mem_map(pcmcia_chipset_handle_t pch, int kind,
|
|
|
|
bus_addr_t card_addr, bus_size_t size, struct pcmcia_mem_handle *pcmhp,
|
2009-02-14 01:39:37 +03:00
|
|
|
bus_size_t *offsetp, int *windowp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t busaddr;
|
|
|
|
long card_offset;
|
|
|
|
int win;
|
|
|
|
|
2003-10-23 04:04:03 +04:00
|
|
|
/* Check that the card is still there. */
|
2008-06-27 00:57:10 +04:00
|
|
|
if ((Pcic_read(sc, PCIC_IF_STATUS) & PCIC_IF_STATUS_CARDDETECT_MASK) !=
|
2003-10-23 04:04:03 +04:00
|
|
|
PCIC_IF_STATUS_CARDDETECT_PRESENT)
|
|
|
|
return 1;
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
for (win = 0; win < PCIC_MEM_WINS; ++win) {
|
|
|
|
if ((ph->memalloc & (1 << win)) == 0) {
|
|
|
|
ph->memalloc |= (1 << win);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (win == PCIC_MEM_WINS) {
|
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
*windowp = win;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
/* XXX this is pretty gross */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-04-19 22:24:26 +04:00
|
|
|
if (!bus_space_is_equal(sc->sc_memt, pcmhp->memt)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
panic("pccbb_pcmcia_mem_map memt is bogus");
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
busaddr = pcmhp->addr;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* compute the address offset to the pcmcia address space for the
|
|
|
|
* pcic. this is intentionally signed. The masks and shifts below
|
|
|
|
* will cause TRT to happen in the pcic registers. Deal with making
|
|
|
|
* sure the address is aligned, and return the alignment offset.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
*offsetp = card_addr % PCIC_MEM_PAGESIZE;
|
|
|
|
card_addr -= *offsetp;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
DPRINTF(("pccbb_pcmcia_mem_map window %d bus %lx+%lx+%lx at card addr "
|
|
|
|
"%lx\n", win, (u_long) busaddr, (u_long) * offsetp, (u_long) size,
|
|
|
|
(u_long) card_addr));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* include the offset in the size, and decrement size by one, since
|
|
|
|
* the hw wants start/stop
|
|
|
|
*/
|
|
|
|
size += *offsetp - 1;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
card_offset = (((long)card_addr) - ((long)busaddr));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
ph->mem[win].addr = busaddr;
|
|
|
|
ph->mem[win].size = size;
|
|
|
|
ph->mem[win].offset = card_offset;
|
|
|
|
ph->mem[win].kind = kind;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-26 22:05:48 +04:00
|
|
|
pccbb_pcmcia_do_mem_map(sc, win);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC int pccbb_pcmcia_mem_unmap(pcmcia_chipset_handle_t pch,
|
|
|
|
* int window)
|
|
|
|
*
|
2000-03-14 04:29:30 +03:00
|
|
|
* This function unmaps memory space which mapped by the function
|
1999-10-19 13:29:46 +04:00
|
|
|
* pccbb_pcmcia_mem_map().
|
|
|
|
*/
|
2000-02-05 21:20:08 +03:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_mem_unmap(pcmcia_chipset_handle_t pch, int window)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
|
|
|
struct pcic_handle *ph = &sc->sc_pcmcia_h;
|
2000-02-05 21:20:08 +03:00
|
|
|
int reg;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (window >= PCIC_MEM_WINS) {
|
|
|
|
panic("pccbb_pcmcia_mem_unmap: window out of range");
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2008-06-27 00:57:10 +04:00
|
|
|
reg = Pcic_read(sc, PCIC_ADDRWIN_ENABLE);
|
2000-02-05 21:20:08 +03:00
|
|
|
reg &= ~(1 << window);
|
2008-06-27 00:57:10 +04:00
|
|
|
Pcic_write(sc, PCIC_ADDRWIN_ENABLE, reg);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
ph->memalloc &= ~(1 << window);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void *pccbb_pcmcia_intr_establish(pcmcia_chipset_handle_t pch,
|
|
|
|
* struct pcmcia_function *pf,
|
|
|
|
* int ipl,
|
|
|
|
* int (*func)(void *),
|
|
|
|
* void *arg);
|
|
|
|
*
|
|
|
|
* This function enables PC-Card interrupt. PCCBB uses PCI interrupt line.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC void *
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_intr_establish(pcmcia_chipset_handle_t pch,
|
|
|
|
struct pcmcia_function *pf, int ipl, int (*func)(void *), void *arg)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
|
|
|
if (!(pf->cfe->flags & PCMCIA_CFE_IRQLEVEL)) {
|
|
|
|
/* what should I do? */
|
|
|
|
if ((pf->cfe->flags & PCMCIA_CFE_IRQLEVEL)) {
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("%s does not provide edge nor pulse "
|
2008-06-25 15:42:32 +04:00
|
|
|
"interrupt\n", device_xname(sc->sc_dev)));
|
2000-02-05 21:20:08 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
2005-02-27 03:26:58 +03:00
|
|
|
/*
|
2000-02-05 21:20:08 +03:00
|
|
|
* XXX Noooooo! The interrupt flag must set properly!!
|
|
|
|
* dumb pcmcia driver!!
|
|
|
|
*/
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2011-08-01 15:20:26 +04:00
|
|
|
return pccbb_intr_establish(sc, ipl, func, arg);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* STATIC void pccbb_pcmcia_intr_disestablish(pcmcia_chipset_handle_t pch,
|
|
|
|
* void *ih)
|
|
|
|
*
|
|
|
|
* This function disables PC-Card interrupt.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
STATIC void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_pcmcia_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2008-06-27 00:57:10 +04:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)pch;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-23 10:28:54 +03:00
|
|
|
pccbb_intr_disestablish(sc, ih);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#if rbus
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* static int
|
|
|
|
* pccbb_rbus_cb_space_alloc(cardbus_chipset_tag_t ct, rbus_tag_t rb,
|
|
|
|
* bus_addr_t addr, bus_size_t size,
|
|
|
|
* bus_addr_t mask, bus_size_t align,
|
|
|
|
* int flags, bus_addr_t *addrp;
|
|
|
|
* bus_space_handle_t *bshp)
|
|
|
|
*
|
|
|
|
* This function allocates a portion of memory or io space for
|
|
|
|
* clients. This function is called from CardBus card drivers.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_rbus_cb_space_alloc(cardbus_chipset_tag_t ct, rbus_tag_t rb,
|
|
|
|
bus_addr_t addr, bus_size_t size, bus_addr_t mask, bus_size_t align,
|
|
|
|
int flags, bus_addr_t *addrp, bus_space_handle_t *bshp)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_rbus_cb_space_alloc: addr 0x%lx, size 0x%lx, "
|
|
|
|
"mask 0x%lx, align 0x%lx\n", (unsigned long)addr,
|
|
|
|
(unsigned long)size, (unsigned long)mask, (unsigned long)align));
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (align == 0) {
|
|
|
|
align = size;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(rb->rb_bt, sc->sc_memt)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
if (align < 16) {
|
|
|
|
return 1;
|
|
|
|
}
|
2002-05-31 13:54:52 +04:00
|
|
|
/*
|
|
|
|
* XXX: align more than 0x1000 to avoid overwrapping
|
|
|
|
* memory windows for two or more devices. 0x1000
|
|
|
|
* means memory window's granularity.
|
|
|
|
*
|
|
|
|
* Two or more devices should be able to share same
|
|
|
|
* memory window region. However, overrapping memory
|
|
|
|
* window is not good because some devices, such as
|
|
|
|
* 3Com 3C575[BC], have a broken address decoder and
|
|
|
|
* intrude other's memory region.
|
|
|
|
*/
|
2001-09-30 10:32:02 +04:00
|
|
|
if (align < 0x1000) {
|
|
|
|
align = 0x1000;
|
|
|
|
}
|
2010-04-19 22:24:26 +04:00
|
|
|
} else if (bus_space_is_equal(rb->rb_bt, sc->sc_iot)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
if (align < 4) {
|
|
|
|
return 1;
|
|
|
|
}
|
2000-03-22 12:35:06 +03:00
|
|
|
/* XXX: hack for avoiding ISA image */
|
|
|
|
if (mask < 0x0100) {
|
|
|
|
mask = 0x3ff;
|
|
|
|
addr = 0x300;
|
|
|
|
}
|
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("pccbb_rbus_cb_space_alloc: Bus space tag 0x%lx is "
|
|
|
|
"NOT used. io: 0x%lx, mem: 0x%lx\n",
|
|
|
|
(unsigned long)rb->rb_bt, (unsigned long)sc->sc_iot,
|
|
|
|
(unsigned long)sc->sc_memt));
|
2000-02-05 21:20:08 +03:00
|
|
|
return 1;
|
|
|
|
/* XXX: panic here? */
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
if (rbus_space_alloc(rb, addr, size, mask, align, flags, addrp, bshp)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_normal_dev(sc->sc_dev, "<rbus> no bus space\n");
|
2000-02-05 21:20:08 +03:00
|
|
|
return 1;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_open_win(sc, rb->rb_bt, *addrp, size, *bshp, 0);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
1999-10-19 13:29:46 +04:00
|
|
|
/*
|
|
|
|
* static int
|
|
|
|
* pccbb_rbus_cb_space_free(cardbus_chipset_tag_t *ct, rbus_tag_t rb,
|
|
|
|
* bus_space_handle_t *bshp, bus_size_t size);
|
|
|
|
*
|
|
|
|
* This function is called from CardBus card drivers.
|
|
|
|
*/
|
1999-10-15 10:07:17 +04:00
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_rbus_cb_space_free(cardbus_chipset_tag_t ct, rbus_tag_t rb,
|
|
|
|
bus_space_handle_t bsh, bus_size_t size)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
struct pccbb_softc *sc = (struct pccbb_softc *)ct;
|
|
|
|
bus_space_tag_t bt = rb->rb_bt;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
pccbb_close_win(sc, bt, bsh, size);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(bt, sc->sc_memt)) {
|
|
|
|
} else if (bus_space_is_equal(bt, sc->sc_iot)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
/* XXX: panic here? */
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return rbus_space_free(rb, bsh, size, NULL);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
#endif /* rbus */
|
|
|
|
|
|
|
|
#if rbus
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_open_win(struct pccbb_softc *sc, bus_space_tag_t bst, bus_addr_t addr,
|
|
|
|
bus_size_t size, bus_space_handle_t bsh, int flags)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-03-02 02:40:26 +03:00
|
|
|
struct pccbb_win_chain_head *head;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t align;
|
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
head = &sc->sc_iowindow;
|
2000-02-05 21:20:08 +03:00
|
|
|
align = 0x04;
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(sc->sc_memt, bst)) {
|
2000-03-02 02:40:26 +03:00
|
|
|
head = &sc->sc_memwindow;
|
2000-02-05 21:20:08 +03:00
|
|
|
align = 0x1000;
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("using memory window, 0x%lx 0x%lx 0x%lx\n\n",
|
|
|
|
(unsigned long)sc->sc_iot, (unsigned long)sc->sc_memt,
|
|
|
|
(unsigned long)bst));
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
if (pccbb_winlist_insert(head, addr, size, bsh, flags)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"pccbb_open_win: %s winlist insert failed\n",
|
2000-03-02 02:40:26 +03:00
|
|
|
(head == &sc->sc_memwindow) ? "mem" : "io");
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
pccbb_winset(align, sc, bst);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_close_win(struct pccbb_softc *sc, bus_space_tag_t bst,
|
|
|
|
bus_space_handle_t bsh, bus_size_t size)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-03-02 02:40:26 +03:00
|
|
|
struct pccbb_win_chain_head *head;
|
2000-02-05 21:20:08 +03:00
|
|
|
bus_addr_t align;
|
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
head = &sc->sc_iowindow;
|
2000-02-05 21:20:08 +03:00
|
|
|
align = 0x04;
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(sc->sc_memt, bst)) {
|
2000-03-02 02:40:26 +03:00
|
|
|
head = &sc->sc_memwindow;
|
2000-02-05 21:20:08 +03:00
|
|
|
align = 0x1000;
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
if (pccbb_winlist_delete(head, bsh, size)) {
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"pccbb_close_win: %s winlist delete failed\n",
|
2000-03-02 02:40:26 +03:00
|
|
|
(head == &sc->sc_memwindow) ? "mem" : "io");
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
|
|
|
pccbb_winset(align, sc, bst);
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-02-05 21:20:08 +03:00
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_winlist_insert(struct pccbb_win_chain_head *head, bus_addr_t start,
|
|
|
|
bus_size_t size, bus_space_handle_t bsh, int flags)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-03-02 02:40:26 +03:00
|
|
|
struct pccbb_win_chain *chainp, *elem;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
if ((elem = malloc(sizeof(struct pccbb_win_chain), M_DEVBUF,
|
|
|
|
M_NOWAIT)) == NULL)
|
2000-03-14 13:26:10 +03:00
|
|
|
return (1); /* fail */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
elem->wc_start = start;
|
|
|
|
elem->wc_end = start + (size - 1);
|
|
|
|
elem->wc_handle = bsh;
|
|
|
|
elem->wc_flags = flags;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2007-11-24 10:59:21 +03:00
|
|
|
TAILQ_FOREACH(chainp, head, wc_list) {
|
|
|
|
if (chainp->wc_end >= start)
|
|
|
|
break;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
2007-11-24 10:59:21 +03:00
|
|
|
if (chainp != NULL)
|
|
|
|
TAILQ_INSERT_AFTER(head, chainp, elem, wc_list);
|
|
|
|
else
|
|
|
|
TAILQ_INSERT_TAIL(head, elem, wc_list);
|
2000-03-14 13:26:10 +03:00
|
|
|
return (0);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_winlist_delete(struct pccbb_win_chain_head *head, bus_space_handle_t bsh,
|
|
|
|
bus_size_t size)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-03-02 02:40:26 +03:00
|
|
|
struct pccbb_win_chain *chainp;
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2007-11-24 10:59:21 +03:00
|
|
|
TAILQ_FOREACH(chainp, head, wc_list) {
|
|
|
|
if (memcmp(&chainp->wc_handle, &bsh, sizeof(bsh)) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (chainp == NULL)
|
|
|
|
return 1; /* fail: no candidate to remove */
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2007-11-24 10:59:21 +03:00
|
|
|
if ((chainp->wc_end - chainp->wc_start) != (size - 1)) {
|
|
|
|
printf("pccbb_winlist_delete: window 0x%lx size "
|
|
|
|
"inconsistent: 0x%lx, 0x%lx\n",
|
|
|
|
(unsigned long)chainp->wc_start,
|
|
|
|
(unsigned long)(chainp->wc_end - chainp->wc_start),
|
|
|
|
(unsigned long)(size - 1));
|
|
|
|
return 1;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2007-11-24 10:59:21 +03:00
|
|
|
TAILQ_REMOVE(head, chainp, wc_list);
|
|
|
|
free(chainp, M_DEVBUF);
|
|
|
|
|
|
|
|
return 0;
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2007-02-04 08:34:38 +03:00
|
|
|
pccbb_winset(bus_addr_t align, struct pccbb_softc *sc, bus_space_tag_t bst)
|
1999-10-15 10:07:17 +04:00
|
|
|
{
|
2000-02-05 21:20:08 +03:00
|
|
|
pci_chipset_tag_t pc;
|
|
|
|
pcitag_t tag;
|
|
|
|
bus_addr_t mask = ~(align - 1);
|
|
|
|
struct {
|
2010-02-25 02:38:40 +03:00
|
|
|
pcireg_t win_start;
|
|
|
|
pcireg_t win_limit;
|
2000-02-05 21:20:08 +03:00
|
|
|
int win_flags;
|
|
|
|
} win[2];
|
|
|
|
struct pccbb_win_chain *chainp;
|
|
|
|
int offs;
|
|
|
|
|
2001-02-22 13:39:31 +03:00
|
|
|
win[0].win_start = win[1].win_start = 0xffffffff;
|
|
|
|
win[0].win_limit = win[1].win_limit = 0;
|
|
|
|
win[0].win_flags = win[1].win_flags = 0;
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
chainp = TAILQ_FIRST(&sc->sc_iowindow);
|
2008-01-14 06:01:41 +03:00
|
|
|
offs = PCI_CB_IOBASE0;
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(sc->sc_memt, bst)) {
|
2000-03-02 02:40:26 +03:00
|
|
|
chainp = TAILQ_FIRST(&sc->sc_memwindow);
|
2008-01-14 06:01:41 +03:00
|
|
|
offs = PCI_CB_MEMBASE0;
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
if (chainp != NULL) {
|
2000-02-05 21:20:08 +03:00
|
|
|
win[0].win_start = chainp->wc_start & mask;
|
|
|
|
win[0].win_limit = chainp->wc_end & mask;
|
|
|
|
win[0].win_flags = chainp->wc_flags;
|
2000-03-02 02:40:26 +03:00
|
|
|
chainp = TAILQ_NEXT(chainp, wc_list);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
for (; chainp != NULL; chainp = TAILQ_NEXT(chainp, wc_list)) {
|
2000-02-05 21:20:08 +03:00
|
|
|
if (win[1].win_start == 0xffffffff) {
|
|
|
|
/* window 1 is not used */
|
|
|
|
if ((win[0].win_flags == chainp->wc_flags) &&
|
|
|
|
(win[0].win_limit + align >=
|
|
|
|
(chainp->wc_start & mask))) {
|
2000-03-02 02:40:26 +03:00
|
|
|
/* concatenate */
|
2000-02-05 21:20:08 +03:00
|
|
|
win[0].win_limit = chainp->wc_end & mask;
|
|
|
|
} else {
|
|
|
|
/* make new window */
|
|
|
|
win[1].win_start = chainp->wc_start & mask;
|
|
|
|
win[1].win_limit = chainp->wc_end & mask;
|
|
|
|
win[1].win_flags = chainp->wc_flags;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2000-03-14 04:29:30 +03:00
|
|
|
/* Both windows are engaged. */
|
2000-02-05 21:20:08 +03:00
|
|
|
if (win[0].win_flags == win[1].win_flags) {
|
|
|
|
/* same flags */
|
|
|
|
if (win[0].win_flags == chainp->wc_flags) {
|
|
|
|
if (win[1].win_start - (win[0].win_limit +
|
|
|
|
align) <
|
|
|
|
(chainp->wc_start & mask) -
|
|
|
|
((chainp->wc_end & mask) + align)) {
|
|
|
|
/*
|
|
|
|
* merge window 0 and 1, and set win1
|
|
|
|
* to chainp
|
|
|
|
*/
|
|
|
|
win[0].win_limit = win[1].win_limit;
|
|
|
|
win[1].win_start =
|
|
|
|
chainp->wc_start & mask;
|
|
|
|
win[1].win_limit =
|
|
|
|
chainp->wc_end & mask;
|
|
|
|
} else {
|
|
|
|
win[1].win_limit =
|
|
|
|
chainp->wc_end & mask;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* different flags */
|
|
|
|
|
2000-03-02 02:40:26 +03:00
|
|
|
/* concatenate win0 and win1 */
|
2000-02-05 21:20:08 +03:00
|
|
|
win[0].win_limit = win[1].win_limit;
|
|
|
|
/* allocate win[1] to new space */
|
|
|
|
win[1].win_start = chainp->wc_start & mask;
|
|
|
|
win[1].win_limit = chainp->wc_end & mask;
|
|
|
|
win[1].win_flags = chainp->wc_flags;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* the flags of win[0] and win[1] is different */
|
|
|
|
if (win[0].win_flags == chainp->wc_flags) {
|
|
|
|
win[0].win_limit = chainp->wc_end & mask;
|
|
|
|
/*
|
|
|
|
* XXX this creates overlapping windows, so
|
|
|
|
* what should the poor bridge do if one is
|
|
|
|
* cachable, and the other is not?
|
|
|
|
*/
|
2008-06-25 15:42:32 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-02-02 00:13:44 +03:00
|
|
|
"overlapping windows\n");
|
2000-02-05 21:20:08 +03:00
|
|
|
} else {
|
|
|
|
win[1].win_limit = chainp->wc_end & mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pc = sc->sc_pc;
|
|
|
|
tag = sc->sc_tag;
|
|
|
|
pci_conf_write(pc, tag, offs, win[0].win_start);
|
|
|
|
pci_conf_write(pc, tag, offs + 4, win[0].win_limit);
|
|
|
|
pci_conf_write(pc, tag, offs + 8, win[1].win_start);
|
|
|
|
pci_conf_write(pc, tag, offs + 12, win[1].win_limit);
|
2003-12-19 22:00:00 +03:00
|
|
|
DPRINTF(("--pccbb_winset: win0 [0x%lx, 0x%lx), win1 [0x%lx, 0x%lx)\n",
|
|
|
|
(unsigned long)pci_conf_read(pc, tag, offs),
|
|
|
|
(unsigned long)pci_conf_read(pc, tag, offs + 4) + align,
|
|
|
|
(unsigned long)pci_conf_read(pc, tag, offs + 8),
|
|
|
|
(unsigned long)pci_conf_read(pc, tag, offs + 12) + align));
|
2000-02-05 21:20:08 +03:00
|
|
|
|
2010-04-19 22:24:26 +04:00
|
|
|
if (bus_space_is_equal(bst, sc->sc_memt)) {
|
2007-08-11 04:31:04 +04:00
|
|
|
pcireg_t bcr = pci_conf_read(pc, tag, PCI_BRIDGE_CONTROL_REG);
|
2001-02-22 13:39:31 +03:00
|
|
|
|
|
|
|
bcr &= ~(CB_BCR_PREFETCH_MEMWIN0 | CB_BCR_PREFETCH_MEMWIN1);
|
|
|
|
if (win[0].win_flags & PCCBB_MEM_CACHABLE)
|
2000-02-05 21:20:08 +03:00
|
|
|
bcr |= CB_BCR_PREFETCH_MEMWIN0;
|
2001-02-22 13:39:31 +03:00
|
|
|
if (win[1].win_flags & PCCBB_MEM_CACHABLE)
|
2000-02-05 21:20:08 +03:00
|
|
|
bcr |= CB_BCR_PREFETCH_MEMWIN1;
|
2007-08-11 04:31:04 +04:00
|
|
|
pci_conf_write(pc, tag, PCI_BRIDGE_CONTROL_REG, bcr);
|
2000-02-05 21:20:08 +03:00
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* rbus */
|
2000-02-22 05:35:26 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static bool
|
2010-02-25 01:37:54 +03:00
|
|
|
pccbb_suspend(device_t dv, const pmf_qual_t *qual)
|
2000-02-22 05:35:26 +03:00
|
|
|
{
|
2007-12-09 23:27:42 +03:00
|
|
|
struct pccbb_softc *sc = device_private(dv);
|
2000-02-22 05:35:26 +03:00
|
|
|
bus_space_tag_t base_memt = sc->sc_base_memt; /* socket regs memory */
|
|
|
|
bus_space_handle_t base_memh = sc->sc_base_memh;
|
2007-12-09 23:27:42 +03:00
|
|
|
pcireg_t reg;
|
2000-02-22 05:35:26 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
if (sc->sc_pil_intr_enable)
|
|
|
|
(void)pccbbintr_function(sc);
|
2010-04-21 03:39:10 +04:00
|
|
|
sc->sc_pil_intr_enable = false;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
reg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_MASK);
|
|
|
|
/* Disable interrupts. */
|
|
|
|
reg &= ~(CB_SOCKET_MASK_CSTS | CB_SOCKET_MASK_CD | CB_SOCKET_MASK_POWER);
|
|
|
|
bus_space_write_4(base_memt, base_memh, CB_SOCKET_MASK, reg);
|
|
|
|
/* XXX joerg Disable power to the socket? */
|
2000-04-06 13:11:57 +04:00
|
|
|
|
2008-02-02 03:31:25 +03:00
|
|
|
/* XXX flush PCI write */
|
|
|
|
bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT);
|
|
|
|
|
|
|
|
/* reset interrupt */
|
|
|
|
bus_space_write_4(base_memt, base_memh, CB_SOCKET_EVENT,
|
|
|
|
bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT));
|
|
|
|
/* XXX flush PCI write */
|
|
|
|
bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT);
|
|
|
|
|
|
|
|
if (sc->sc_ih != NULL) {
|
|
|
|
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
|
|
|
|
sc->sc_ih = NULL;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
|
|
|
}
|
2006-06-17 21:06:51 +04:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static bool
|
2010-02-25 01:37:54 +03:00
|
|
|
pccbb_resume(device_t dv, const pmf_qual_t *qual)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct pccbb_softc *sc = device_private(dv);
|
|
|
|
bus_space_tag_t base_memt = sc->sc_base_memt; /* socket regs memory */
|
|
|
|
bus_space_handle_t base_memh = sc->sc_base_memh;
|
|
|
|
pcireg_t reg;
|
2005-01-27 05:44:59 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
pccbb_chipinit(sc);
|
2008-02-02 03:31:25 +03:00
|
|
|
pccbb_intrinit(sc);
|
2007-12-09 23:27:42 +03:00
|
|
|
/* setup memory and io space window for CB */
|
|
|
|
pccbb_winset(0x1000, sc, sc->sc_memt);
|
|
|
|
pccbb_winset(0x04, sc, sc->sc_iot);
|
|
|
|
|
|
|
|
/* CSC Interrupt: Card detect interrupt on */
|
|
|
|
reg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_MASK);
|
|
|
|
/* Card detect intr is turned on. */
|
2008-02-02 03:31:25 +03:00
|
|
|
reg |= CB_SOCKET_MASK_CSTS | CB_SOCKET_MASK_CD | CB_SOCKET_MASK_POWER;
|
2007-12-09 23:27:42 +03:00
|
|
|
bus_space_write_4(base_memt, base_memh, CB_SOCKET_MASK, reg);
|
|
|
|
/* reset interrupt */
|
|
|
|
reg = bus_space_read_4(base_memt, base_memh, CB_SOCKET_EVENT);
|
|
|
|
bus_space_write_4(base_memt, base_memh, CB_SOCKET_EVENT, reg);
|
2000-02-22 05:35:26 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
/*
|
|
|
|
* check for card insertion or removal during suspend period.
|
|
|
|
* XXX: the code can't cope with card swap (remove then
|
|
|
|
* insert). how can we detect such situation?
|
|
|
|
*/
|
|
|
|
(void)pccbbintr(sc);
|
2000-04-06 13:11:57 +04:00
|
|
|
|
2010-04-21 03:39:10 +04:00
|
|
|
sc->sc_pil_intr_enable = true;
|
2000-04-06 13:11:57 +04:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
2000-02-22 05:35:26 +03:00
|
|
|
}
|