/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
*/
/* * This callback should (re-)initialise the socket, turn on status * interrupts and PCMCIA bus, and wait for power to stabilise so that * the card status signals report correctly. * * Hardware cannot do that.
*/ staticint bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock)
{ return 0;
}
/* * This callback should remove power on the socket, disable IRQs from * the card, turn off status interrupts, and disable the PCMCIA bus. * * Hardware cannot do that.
*/ staticint bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock)
{ return 0;
}
/* * Implements the set_socket() operation for the in-kernel PCMCIA * service (formerly SS_SetSocket in Card Services). We more or * less punt all of this work and let the kernel handle the details * of power configuration, reset, &c. We also record the value of * `state' in order to regurgitate it to the PCMCIA core later.
*/ staticint bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock,
socket_state_t *state)
{ struct bcm63xx_pcmcia_socket *skt; unsignedlong flags;
u32 val;
skt = sock->driver_data;
spin_lock_irqsave(&skt->lock, flags);
/* note: hardware cannot control socket power, so we will
* always report SS_POWERON */
/* apply socket reset */
val = pcmcia_readl(skt, PCMCIA_C1_REG); if (state->flags & SS_RESET)
val |= PCMCIA_C1_RESET_MASK; else
val &= ~PCMCIA_C1_RESET_MASK;
/* reverse reset logic for cardbus card */ if (skt->card_detected && (skt->card_type & CARD_CARDBUS))
val ^= PCMCIA_C1_RESET_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* keep requested state for event reporting */
skt->requested_state = *state;
spin_unlock_irqrestore(&skt->lock, flags);
return 0;
}
/* * identity cardtype from VS[12] input, CD[12] input while only VS2 is * floating, and CD[12] input while only VS1 is floating
*/ enum {
IN_VS1 = (1 << 0),
IN_VS2 = (1 << 1),
IN_CD1_VS2H = (1 << 2),
IN_CD2_VS2H = (1 << 3),
IN_CD1_VS1H = (1 << 4),
IN_CD2_VS1H = (1 << 5),
};
/* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */
[IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */
};
/* * poll hardware to check card insertion status
*/ staticunsignedint __get_socket_status(struct bcm63xx_pcmcia_socket *skt)
{ unsignedint stat;
u32 val;
stat = 0;
/* check CD for card presence */
val = pcmcia_readl(skt, PCMCIA_C1_REG);
if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK))
stat |= SS_DETECT;
/* if new insertion, detect cardtype */ if ((stat & SS_DETECT) && !skt->card_detected) { unsignedint stat = 0;
/* float VS1, float VS2 */
val |= PCMCIA_C1_VS1OE_MASK;
val |= PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read VS[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0;
stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0;
/* drive VS1 low, float VS2 */
val &= ~PCMCIA_C1_VS1OE_MASK;
val |= PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read CD[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0;
stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0;
/* float VS1, drive VS2 low */
val |= PCMCIA_C1_VS1OE_MASK;
val &= ~PCMCIA_C1_VS2OE_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* wait for output to stabilize and read CD[12] */
udelay(10);
val = pcmcia_readl(skt, PCMCIA_C1_REG);
stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0;
stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0;
/* guess cardtype from all this */
skt->card_type = vscd_to_cardtype[stat]; if (!skt->card_type)
dev_err(&skt->socket.dev, "unsupported card type\n");
/* drive both VS pin to 0 again */
val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK);
/* enable correct logic */
val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK); if (skt->card_type & CARD_PCCARD)
val |= PCMCIA_C1_EN_PCMCIA_MASK; else
val |= PCMCIA_C1_EN_CARDBUS_MASK;
/* report card type/voltage */ if (skt->card_type & CARD_CARDBUS)
stat |= SS_CARDBUS; if (skt->card_type & CARD_3V)
stat |= SS_3VCARD; if (skt->card_type & CARD_XV)
stat |= SS_XVCARD;
stat |= SS_POWERON;
if (gpio_get_value(skt->pd->ready_gpio))
stat |= SS_READY;
return stat;
}
/* * core request to get current socket status
*/ staticint bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock, unsignedint *status)
{ struct bcm63xx_pcmcia_socket *skt;
/* keep only changed bits, and mask with required one from the
* core */
events = (stat ^ skt->old_status) & skt->requested_state.csc_mask;
skt->old_status = stat;
spin_unlock_bh(&skt->lock);
if (events)
pcmcia_parse_events(&skt->socket, events);
staticint bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map)
{ /* this doesn't seem to be called by pcmcia layer if static
* mapping is used */ return 0;
}
/* initialize pcmcia control register, drive VS[12] to 0, * leave CB IDSEL to the old value since it is set by the PCI
* layer */
val = pcmcia_readl(skt, PCMCIA_C1_REG);
val &= PCMCIA_C1_CBIDSEL_MASK;
val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK;
pcmcia_writel(skt, val, PCMCIA_C1_REG);
/* * Hardware has only one set of timings registers, not one for * each memory access type, so we configure them for the * slowest one: attribute memory.
*/
val = PCMCIA_C2_DATA16_MASK;
val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT;
val |= 6 << PCMCIA_C2_INACTIVE_SHIFT;
val |= 3 << PCMCIA_C2_SETUP_SHIFT;
val |= 3 << PCMCIA_C2_HOLD_SHIFT;
pcmcia_writel(skt, val, PCMCIA_C2_REG);
ret = pcmcia_register_socket(sock); if (ret) goto err;
err: if (skt->io_base)
iounmap(skt->io_base); if (skt->base)
iounmap(skt->base); if (skt->reg_res)
release_mem_region(skt->reg_res->start, regmem_size);
kfree(skt); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.