// SPDX-License-Identifier: GPL-2.0-only /* * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards. * * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> *
*/
/* This is a fairly generic PCMCIA socket driver suitable for the * following Alchemy Development boards: * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200, Db1300 * * The Db1000 is used as a reference: Per-socket card-, carddetect- and * statuschange IRQs connected to SoC GPIOs, control and status register * bits arranged in per-socket groups in an external PLD. All boards * listed here use this layout, including bit positions and meanings. * Of course there are exceptions in later boards: * * - Pb1100/Pb1500: single socket only; voltage key bits VS are * at STATUS[5:4] (instead of STATUS[1:0]). * - Au1200-based: additional card-eject irqs, irqs not gpios! * - Db1300: Db1200-like, no pwr ctrl, single socket (#1).
*/
/* previous flags for set_socket() */ unsignedint old_flags;
/* interrupt sources: linux irq numbers! */ int insert_irq; /* default carddetect irq */ int stschg_irq; /* card-status-change irq */ int card_irq; /* card irq */ int eject_irq; /* db1200/pb1200 have these */ int insert_gpio; /* db1000 carddetect gpio */
#define BOARD_TYPE_DEFAULT 0 /* most boards */ #define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ #define BOARD_TYPE_PB1100 2 /* VS bits slightly different */ #define BOARD_TYPE_DB1300 3 /* no power control */ int board_type;
};
/* STSCHG tends to bounce heavily when cards are inserted/ejected. * To avoid this, the interrupt is normally disabled and only enabled * after reset to a card has been de-asserted.
*/ staticinlinevoid set_stschg(struct db1x_pcmcia_sock *sock, int en)
{ if (sock->stschg_irq != -1) { if (en)
enable_irq(sock->stschg_irq); else
disable_irq(sock->stschg_irq);
}
}
/* Db/Pb1200 have separate per-socket insertion and ejection * interrupts which stay asserted as long as the card is * inserted/missing. The one which caused us to be called * needs to be disabled and the other one enabled.
*/ static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data)
{
disable_irq_nosync(irq); return IRQ_WAKE_THREAD;
}
/* Wait a bit for the signals to stop bouncing. */
msleep(100); if (irq == sock->insert_irq)
enable_irq(sock->eject_irq); else
enable_irq(sock->insert_irq);
pcmcia_parse_events(&sock->socket, SS_DETECT);
return IRQ_HANDLED;
}
staticint db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
{ int ret;
if (sock->stschg_irq != -1) {
ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq,
0, "pcmcia_stschg", sock); if (ret) return ret;
}
/* Db/Pb1200 have separate per-socket insertion and ejection * interrupts, which should show edge behaviour but don't. * So interrupts are disabled until both insertion and * ejection handler have been registered and the currently * active one disabled.
*/ if ((sock->board_type == BOARD_TYPE_DB1200) ||
(sock->board_type == BOARD_TYPE_DB1300)) {
ret = request_threaded_irq(sock->insert_irq, db1200_pcmcia_cdirq,
db1200_pcmcia_cdirq_fn, 0, "pcmcia_insert", sock); if (ret) goto out1;
ret = request_threaded_irq(sock->eject_irq, db1200_pcmcia_cdirq,
db1200_pcmcia_cdirq_fn, 0, "pcmcia_eject", sock); if (ret) {
free_irq(sock->insert_irq, sock); goto out1;
}
/* enable the currently silent one */ if (db1x_card_inserted(sock))
enable_irq(sock->eject_irq); else
enable_irq(sock->insert_irq);
} else { /* all other (older) Db1x00 boards use a GPIO to show * card detection status: use both-edge triggers.
*/
irq_set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH);
ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq,
0, "pcmcia_carddetect", sock);
if (ret) goto out1;
}
return 0; /* all done */
out1: if (sock->stschg_irq != -1)
free_irq(sock->stschg_irq, sock);
free_irq(sock->insert_irq, sock); if (sock->eject_irq != -1)
free_irq(sock->eject_irq, sock);
}
/* * configure a PCMCIA socket on the Db1x00 series of boards (and * compatibles). * * 2 external registers are involved: * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id * pcmcia_control(offset 0x10): * bits[0:1] set vcc for card * bits[2:3] set vpp for card * bit 4: enable data buffers * bit 7: reset# for card * add 8 for second socket.
*/ staticint db1x_pcmcia_configure(struct pcmcia_socket *skt, struct socket_state_t *state)
{ struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); unsignedshort cr_clr, cr_set; unsignedint changed; int v, p, ret;
/* card voltage setup */
cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */
cr_set = 0;
v = p = ret = 0;
switch (state->Vcc) { case 50:
++v;
fallthrough; case 33:
++v;
fallthrough; case 0: break; default:
printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n",
sock->nr, state->Vcc);
}
switch (state->Vpp) { case 12:
++p;
fallthrough; case 33: case 50:
++p;
fallthrough; case 0: break; default:
printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n",
sock->nr, state->Vpp);
}
/* sanity check: Vpp must be 0, 12, or Vcc */ if (((state->Vcc == 33) && (state->Vpp == 50)) ||
((state->Vcc == 50) && (state->Vpp == 33))) {
printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n",
sock->nr, state->Vcc, state->Vpp);
v = p = 0;
ret = -EINVAL;
}
/* create new voltage code */ if (sock->board_type != BOARD_TYPE_DB1300)
cr_set |= ((v << 2) | p) << (sock->nr * 8);
/* reset was taken away: give card time to initialize properly */ if ((changed & SS_RESET) && !(state->flags & SS_RESET)) {
msleep(500);
set_stschg(sock, 1);
}
status = db1x_card_inserted(sock) ? SS_DETECT : 0;
cr = bcsr_read(BCSR_PCMCIA);
sr = bcsr_read(BCSR_STATUS);
/* PB1100/PB1500: voltage key bits are at [5:4] */ if (sock->board_type == BOARD_TYPE_PB1100)
sr >>= 4;
/* determine card type */ switch (GET_VS(sr, sock->nr)) { case 0: case 2:
status |= SS_3VCARD; /* 3V card */ break; case 3: break; /* 5V card: set nothing */ default:
status |= SS_XVCARD; /* treated as unsupported in core */
}
/* if Vcc is not zero, we have applied power to a card */
status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0;
/* DB1300: power always on, but don't tell when no card present */ if ((sock->board_type == BOARD_TYPE_DB1300) && (status & SS_DETECT))
status = SS_POWERON | SS_3VCARD | SS_DETECT;
/* reset de-asserted? then we're ready */
status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET;
sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); if (!sock) return -ENOMEM;
sock->nr = pdev->id;
bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); switch (bid) { case BCSR_WHOAMI_PB1500: case BCSR_WHOAMI_PB1500R2: case BCSR_WHOAMI_PB1100:
sock->board_type = BOARD_TYPE_PB1100; break; case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR:
sock->board_type = BOARD_TYPE_DEFAULT; break; case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200:
sock->board_type = BOARD_TYPE_DB1200; break; case BCSR_WHOAMI_DB1300:
sock->board_type = BOARD_TYPE_DB1300; break; default:
printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid);
ret = -ENODEV; goto out0;
}
/* * gather resources necessary and optional nice-to-haves to * operate a socket: * This includes IRQs for Carddetection/ejection, the card * itself and optional status change detection. * Also, the memory areas covered by a socket. For these * we require the real 36bit addresses (see the au1000.h * header for more information).
*/
/* card: irq assigned to the card itself. */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
sock->card_irq = r ? r->start : 0;
/* insert: irq which triggers on card insertion/ejection * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here!
*/
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert");
sock->insert_irq = r ? r->start : -1; if (sock->board_type == BOARD_TYPE_DEFAULT) {
sock->insert_gpio = r ? r->start : -1;
sock->insert_irq = r ? gpio_to_irq(r->start) : -1;
}
/* stschg: irq which trigger on card status change (optional) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
sock->stschg_irq = r ? r->start : -1;
/* eject: irq which triggers on ejection (DB1200/PB1200 only) */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject");
sock->eject_irq = r ? r->start : -1;
ret = -ENODEV;
/* 36bit PCMCIA Attribute area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n",
sock->nr); goto out0;
}
sock->phys_attr = r->start;
/* 36bit PCMCIA Memory area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
sock->nr); goto out0;
}
sock->phys_mem = r->start;
/* 36bit PCMCIA IO area address */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); if (!r) {
printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n",
sock->nr); goto out0;
}
sock->phys_io = r->start;
/* * PCMCIA client drivers use the inb/outb macros to access * the IO registers. Since mips_io_port_base is added * to the access address of the mips implementation of * inb/outb, we need to subtract it here because we want * to access the I/O or MEM address directly, without * going through this "mips_io_port_base" mechanism.
*/
sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
mips_io_port_base);
if (!sock->virt_io) {
printk(KERN_ERR "pcmcia%d: cannot remap IO area\n",
sock->nr);
ret = -ENOMEM; goto out0;
}
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.