/*
* Copyright (c) 1997, 1998, 1999 and 2000
* HAYAKAWA Koichi. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
}
STATIC int
cardbusdetach(device_t self, int flags)
{
int rc;
if ((rc = config_detach_children(self, flags)) != 0)
return rc;
pmf_device_deregister(self);
return 0;
}
static int
cardbus_read_tuples(struct cardbus_attach_args *ca, pcireg_t cis_ptr,
u_int8_t *tuples, size_t len)
{
struct cardbus_softc *sc = ca->ca_ct->ct_sc;
cardbus_chipset_tag_t cc = ca->ca_ct->ct_cc;
cardbus_function_tag_t cf = ca->ca_ct->ct_cf;
pcitag_t tag = ca->ca_tag;
pcireg_t command;
bus_space_tag_t bar_tag;
bus_space_handle_t bar_memh;
bus_size_t bar_size;
bus_addr_t bar_addr;
pcireg_t reg;
int found = 0;
int cardbus_space = cis_ptr & CARDBUS_CIS_ASIMASK;
size_t mlen, n, tlen;
int i, j;
memset(tuples, 0, len);
cis_ptr = cis_ptr & CARDBUS_CIS_ADDRMASK;
switch (cardbus_space) {
case CARDBUS_CIS_ASI_TUPLE:
DPRINTF(("%s: reading CIS data from configuration space\n",
device_xname(sc->sc_dev)));
for (i = cis_ptr, j = 0; i < 0xff; i += 4) {
u_int32_t e = (*cf->cardbus_conf_read)(cc, tag, i);
tuples[j] = 0xff & e;
e >>= 8;
tuples[j + 1] = 0xff & e;
e >>= 8;
tuples[j + 2] = 0xff & e;
e >>= 8;
tuples[j + 3] = 0xff & e;
j += 4;
}
found++;
break;
case CARDBUS_CIS_ASI_BAR0:
case CARDBUS_CIS_ASI_BAR1:
case CARDBUS_CIS_ASI_BAR2:
case CARDBUS_CIS_ASI_BAR3:
case CARDBUS_CIS_ASI_BAR4:
case CARDBUS_CIS_ASI_BAR5:
case CARDBUS_CIS_ASI_ROM:
if (cardbus_space == CARDBUS_CIS_ASI_ROM) {
reg = CARDBUS_ROM_REG;
DPRINTF(("%s: reading CIS data from ROM\n",
device_xname(sc->sc_dev)));
} else {
reg = CARDBUS_CIS_ASI_BAR(cardbus_space);
DPRINTF(("%s: reading CIS data from BAR%d\n",
device_xname(sc->sc_dev), cardbus_space - 1));
}
/*
* XXX zero register so mapreg_map doesn't get confused by old
* contents.
*/
cardbus_conf_write(cc, cf, tag, reg, 0);
if (Cardbus_mapreg_map(ca->ca_ct, reg,
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
0, &bar_tag, &bar_memh, &bar_addr, &bar_size)) {
aprint_error_dev(sc->sc_dev, "failed to map memory\n");
return (1);
}
aprint_debug_dev(sc->sc_dev, "mapped %ju bytes at 0x%jx\n",
(uintmax_t)bar_size, (uintmax_t)bar_addr);
if (cardbus_space == CARDBUS_CIS_ASI_ROM) {
pcireg_t exrom;
int save;
struct cardbus_rom_image_head rom_image;
struct cardbus_rom_image *p;
case PCMCIA_CISTPL_VERS_1:
memcpy(cis->cis1_info_buf, tuple + 2, tuple[1]);
i = 0;
p = cis->cis1_info_buf + 2;
while (i <
sizeof(cis->cis1_info) / sizeof(cis->cis1_info[0])) {
if (p >= cis->cis1_info_buf + tuple[1] || *p == '\xff')
break;
cis->cis1_info[i++] = p;
while (*p != '\0' && *p != '\xff')
p++;
if (*p == '\0')
p++;
}
break;
case PCMCIA_CISTPL_BAR:
if (tuple[1] != 6) {
DPRINTF(("%s: BAR with short length (%d)\n",
__func__, tuple[1]));
break;
}
bar_index = tuple[2] & 7;
if (bar_index == 0) {
DPRINTF(("%s: invalid ASI in BAR tuple\n", __func__));
break;
}
bar_index--;
cis->bar[bar_index].flags = tuple[2];
cis->bar[bar_index].size =
(tuple[4] << 0) |
(tuple[5] << 8) |
(tuple[6] << 16) |
(tuple[7] << 24);
break;
case PCMCIA_CISTPL_FUNCID:
cis->funcid = tuple[2];
break;
case PCMCIA_CISTPL_FUNCE:
switch (cis->funcid) {
case PCMCIA_FUNCTION_SERIAL:
if (tuple[1] >= 2 &&
/* XXX PCMCIA_TPLFE_TYPE_SERIAL_??? */
tuple[2] == 0) {
cis->funce.serial.uart_type = tuple[3] & 0x1f;
cis->funce.serial.uart_present = 1;
}
break;
case PCMCIA_FUNCTION_NETWORK:
if (tuple[1] >= 8 &&
tuple[2] == PCMCIA_TPLFE_TYPE_LAN_NID) {
if (tuple[3] >
sizeof(cis->funce.network.netid)) {
DPRINTF(("%s: unknown network id type "
"(len = %d)\n",
__func__, tuple[3]));
} else {
cis->funce.network.netid_present = 1;
memcpy(cis->funce.network.netid,
tuple + 4, tuple[3]);
}
}
break;
}
break;
}
}
/*
* int cardbus_attach_card(struct cardbus_softc *sc)
*
* This function attaches the card on the slot: turns on power,
* reads and analyses tuple, sets configuration index.
*
* This function returns the number of recognised device functions.
* If no functions are recognised, return 0.
*/
int
cardbus_attach_card(struct cardbus_softc *sc)
{
cardbus_chipset_tag_t cc;
cardbus_function_tag_t cf;
int cdstatus;
static int wildcard[CARDBUSCF_NLOCS] = {
CARDBUSCF_FUNCTION_DEFAULT
};
/* set initial latency and cacheline size */
bhlc = cardbus_conf_read(cc, cf, tag, PCI_BHLC_REG);
icr = cardbus_conf_read(cc, cf, tag, PCI_INTERRUPT_REG);
DPRINTF(("%s func%d icr 0x%08x bhlc 0x%08x -> ",
device_xname(sc->sc_dev), function, icr, bhlc));
bhlc &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT);
bhlc |= (sc->sc_cacheline & PCI_CACHELINE_MASK) <<
PCI_CACHELINE_SHIFT;
/*
* Set the initial value of the Latency Timer.
*
* While a PCI device owns the bus, its Latency
* Timer counts down bus cycles from its initial
* value to 0. Minimum Grant tells for how long
* the device wants to own the bus once it gets
* access, in units of 250ns.
*
* On a 33 MHz bus, there are 8 cycles per 250ns.
* So I multiply the Minimum Grant by 8 to find
* out the initial value of the Latency Timer.
*
* Avoid setting a Latency Timer less than 0x10,
* since the old code did not do that.
*/
lattimer =
MIN(sc->sc_max_lattimer, MAX(0x10, 8 * PCI_MIN_GNT(icr)));
if (PCI_LATTIMER(bhlc) < lattimer) {
bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
bhlc |= (lattimer << PCI_LATTIMER_SHIFT);
}
/*
* We need to allocate the ct here, since we might
* need it when reading the CIS
*/
ct = malloc(sizeof(struct cardbus_devfunc),
M_DEVBUF, M_WAITOK);
ct->ct_bhlc = bhlc;
ct->ct_cc = sc->sc_cc;
ct->ct_cf = sc->sc_cf;
ct->ct_bus = sc->sc_bus;
ct->ct_func = function;
ct->ct_sc = sc;
sc->sc_funcs[function] = ct;
if ((csc = config_found(sc->sc_dev, &ca, cardbusprint,
CFARGS(.submatch = config_stdsubmatch,
.locators = locs))) == NULL) {
/* do not match */
disable_function(sc, function);
sc->sc_funcs[function] = NULL;
free(ct, M_DEVBUF);
} else {
/* found */
ct->ct_device = csc;
}
}
/*
* XXX power down pseudo function 8 (this will power down the card
* if no functions were attached).
*/
disable_function(sc, 8);
return (0);
}
static int
cardbusprint(void *aux, const char *pnp)
{
struct cardbus_attach_args *ca = aux;
char devinfo[256];
int i;
if (pnp) {
pci_devinfo(ca->ca_id, ca->ca_class, 1, devinfo,
sizeof(devinfo));
for (i = 0; i < 4; i++) {
if (ca->ca_cis.cis1_info[i] == NULL)
break;
if (i)
aprint_normal(", ");
aprint_normal("%s", ca->ca_cis.cis1_info[i]);
}
aprint_verbose("%s(manufacturer 0x%x, product 0x%x)",
i ? " " : "",
ca->ca_cis.manufacturer, ca->ca_cis.product);
aprint_normal(" %s at %s", devinfo, pnp);
}
aprint_normal(" function %d", ca->ca_function);
return (UNCONF);
}
/*
* void cardbus_detach_card(struct cardbus_softc *sc)
*
* This function detaches the card on the slot: detach device data
* structure and turns off the power.
*
* This function must not be called under interrupt context.
*/
void
cardbus_detach_card(struct cardbus_softc *sc)
{
int f;
struct cardbus_devfunc *ct;
for (f = 0; f < 8; f++) {
ct = sc->sc_funcs[f];
if (!ct)
continue;
/*
* XXX this should be merged with cardbus_function_{enable,disable},
* but we don't have a ct when these functions are called.
*/
static void
enable_function(struct cardbus_softc *sc, int cdstatus, int function)
{
if (sc->sc_poweron_func == 0) {
/* switch to 3V and/or wait for power to stabilize */
if (cdstatus & CARDBUS_3V_CARD) {
/*
* sc_poweron_func must be substituted before
* entering sleep, in order to avoid turn on
* power twice.
*/
sc->sc_poweron_func |= (1 << function);
(*sc->sc_cf->cardbus_power)(sc->sc_cc, CARDBUS_VCC_3V);
} else {
/* No cards other than 3.3V cards. */
return;
}
(*sc->sc_cf->cardbus_ctrl)(sc->sc_cc, CARDBUS_RESET);
}
sc->sc_poweron_func |= (1 << function);
}
static void
disable_function(struct cardbus_softc *sc, int function)
{
bool no_powerdown;
cardbus_devfunc_t ct;
device_t dv;
int i;
sc->sc_poweron_func &= ~(1 << function);
if (sc->sc_poweron_func != 0)
return;
for (i = 0; i < __arraycount(sc->sc_funcs); i++) {
if ((ct = sc->sc_funcs[i]) == NULL)
continue;
dv = ct->ct_device;
if (prop_dictionary_get_bool(device_properties(dv),
"pmf-no-powerdown", &no_powerdown) && no_powerdown)
return;
}
/* power-off because no functions are enabled */
(*sc->sc_cf->cardbus_power)(sc->sc_cc, CARDBUS_VCC_0V);
}
/*
* int cardbus_function_enable(struct cardbus_softc *sc, int func)
*
* This function enables a function on a card. When no power is
* applied on the card, power will be applied on it.
*/
int
cardbus_function_enable(struct cardbus_softc *sc, int func)
{
cardbus_chipset_tag_t cc = sc->sc_cc;
cardbus_function_tag_t cf = sc->sc_cf;
cardbus_devfunc_t ct;
pcireg_t command;
pcitag_t tag;
if ((ct = sc->sc_funcs[func]) != NULL)
Cardbus_conf_write(ct, tag, PCI_BHLC_REG, ct->ct_bhlc);
DPRINTF(("%x\n", sc->sc_poweron_func));
return (0);
}
/*
* int cardbus_function_disable(struct cardbus_softc *, int func)
*
* This function disable a function on a card. When no functions are
* enabled, it turns off the power.
*/
int
cardbus_function_disable(struct cardbus_softc *sc, int func)
{
type = tuple[0];
switch (type) {
case PCMCIA_CISTPL_NULL:
case PCMCIA_CISTPL_END:
len = 1;
break;
default:
if (tuple + 2 > end)
return (NULL);
len = tuple[1] + 2;
break;
}
if (tuple + len > end)
return (NULL);
(*func)(tuple, len, data);
if (type == PCMCIA_CISTPL_END || tuple + len == end)
return (NULL);
return (tuple + len);
}
/*
* XXX: this is another reason why this code should be shared with PCI.
*/
static int
cardbus_get_powerstate_int(cardbus_devfunc_t ct, pcitag_t tag,
pcireg_t *state, int offset)
{
pcireg_t value, now;
cardbus_chipset_tag_t cc = ct->ct_cc;
cardbus_function_tag_t cf = ct->ct_cf;
value = cardbus_conf_read(cc, cf, tag, offset + PCI_PMCSR);
now = value & PCI_PMCSR_STATE_MASK;
switch (now) {
case PCI_PMCSR_STATE_D0:
case PCI_PMCSR_STATE_D1:
case PCI_PMCSR_STATE_D2:
case PCI_PMCSR_STATE_D3:
*state = now;
return 0;
default:
return EINVAL;
}
}
int
cardbus_get_powerstate(cardbus_devfunc_t ct, pcitag_t tag, pcireg_t *state)
{
cardbus_chipset_tag_t cc = ct->ct_cc;
cardbus_function_tag_t cf = ct->ct_cf;
int offset;
pcireg_t value;
if (!cardbus_get_capability(cc, cf, tag, PCI_CAP_PWRMGMT, &offset, &value))
return EOPNOTSUPP;
static int
cardbus_set_powerstate_int(cardbus_devfunc_t ct, pcitag_t tag,
pcireg_t state, int offset, pcireg_t cap_reg)
{
cardbus_chipset_tag_t cc = ct->ct_cc;
cardbus_function_tag_t cf = ct->ct_cf;
pcireg_t value, cap, now;
KASSERT((offset & 0x3) == 0);
cap = cap_reg >> PCI_PMCR_SHIFT;
value = cardbus_conf_read(cc, cf, tag, offset + PCI_PMCSR);
now = value & PCI_PMCSR_STATE_MASK;
value &= ~PCI_PMCSR_STATE_MASK;
if (now == state)
return 0;
switch (state) {
case PCI_PMCSR_STATE_D0:
break;
case PCI_PMCSR_STATE_D1:
if (now == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D3) {
printf("invalid transition from %d to D1\n", (int)now);
return EINVAL;
}
if (!(cap & PCI_PMCR_D1SUPP)) {
printf("D1 not supported\n");
return EOPNOTSUPP;
}
break;
case PCI_PMCSR_STATE_D2:
if (now == PCI_PMCSR_STATE_D3) {
printf("invalid transition from %d to D2\n", (int)now);
return EINVAL;
}
if (!(cap & PCI_PMCR_D2SUPP)) {
printf("D2 not supported\n");
return EOPNOTSUPP;
}
break;
case PCI_PMCSR_STATE_D3:
break;
default:
return EINVAL;
}
value |= state;
cardbus_conf_write(cc, cf, tag, offset + PCI_PMCSR, value);
if (state == PCI_PMCSR_STATE_D3 || now == PCI_PMCSR_STATE_D3)
DELAY(10000);
else if (state == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D2)
DELAY(200);
return 0;
}
int
cardbus_set_powerstate(cardbus_devfunc_t ct, pcitag_t tag, pcireg_t state)
{
cardbus_chipset_tag_t cc = ct->ct_cc;
cardbus_function_tag_t cf = ct->ct_cf;
int offset;
pcireg_t value;
if (!cardbus_get_capability(cc, cf, tag, PCI_CAP_PWRMGMT, &offset,
&value))
return EOPNOTSUPP;
if (0 <= type && type < NAME_LEN(tuple_names)) {
return (tuple_names[type]);
} else if (type == 0xff) {
return ("END");
} else {
return ("Reserved");
}
}
static void
print_tuple(u_int8_t *tuple, int len, void *data)
{
int i;
printf("tuple: %s len %d\n", tuple_name(tuple[0]), len);
for (i = 0; i < len; ++i) {
if (i % 16 == 0) {
printf(" 0x%2x:", i);
}
printf(" %x", tuple[i]);
if (i % 16 == 15) {
printf("\n");
}
}
if (i % 16 != 0) {
printf("\n");
}
}
#endif