// SPDX-License-Identifier: GPL-2.0-only /* * cs.c -- Kernel Card Services - core services * * The initial developer of the original code is David A. Hinds * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds
*/
/** * pcmcia_register_socket - add a new pcmcia socket device * @socket: the &socket to register
*/ int pcmcia_register_socket(struct pcmcia_socket *socket)
{ struct task_struct *tsk; int ret;
if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops) return -EINVAL;
/* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia * sockets... but the socket number is deprecated
* anyways, so I don't care] */
down_write(&pcmcia_socket_list_rwsem); if (list_empty(&pcmcia_socket_list))
socket->sock = 0; else { unsignedint found, i = 1; struct pcmcia_socket *tmp; do {
found = 1;
list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) { if (tmp->sock == i)
found = 0;
}
i++;
} while (!found);
socket->sock = i - 1;
}
list_add_tail(&socket->socket_list, &pcmcia_socket_list);
up_write(&pcmcia_socket_list_rwsem);
#ifndef CONFIG_CARDBUS /* * If we do not support Cardbus, ensure that * the Cardbus socket capability is disabled.
*/
socket->features &= ~SS_CAP_CARDBUS; #endif
/* set proper values in socket->dev */
dev_set_drvdata(&socket->dev, socket);
socket->dev.class = &pcmcia_socket_class;
dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock);
if (socket->resource_ops->init) {
mutex_lock(&socket->ops_mutex);
ret = socket->resource_ops->init(socket);
mutex_unlock(&socket->ops_mutex); if (ret) goto err;
}
tsk = kthread_run(pccardd, socket, "pccardd"); if (IS_ERR(tsk)) {
ret = PTR_ERR(tsk); goto err;
}
wait_for_completion(&socket->thread_done); if (!socket->thread) {
dev_warn(&socket->dev, "PCMCIA: warning: socket thread did not start\n"); return -EIO;
}
pcmcia_parse_events(socket, SS_DETECT);
/* * Let's try to get the PCMCIA module for 16-bit PCMCIA support. * If it fails, it doesn't matter -- we still have 32-bit CardBus * support to offer, so this is not a failure mode.
*/
request_module_nowait("pcmcia");
/* remove from our own list */
down_write(&pcmcia_socket_list_rwsem);
list_del(&socket->socket_list);
up_write(&pcmcia_socket_list_rwsem);
/* wait for sysfs to drop all references */ if (socket->resource_ops->exit) {
mutex_lock(&socket->ops_mutex);
socket->resource_ops->exit(socket);
mutex_unlock(&socket->ops_mutex);
}
wait_for_completion(&socket->socket_released);
} /* pcmcia_unregister_socket */
EXPORT_SYMBOL(pcmcia_unregister_socket);
staticint socket_reset(struct pcmcia_socket *skt)
{ int status, i;
msleep(unreset_delay * 10); for (i = 0; i < unreset_limit; i++) {
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT)) return -ENODEV;
if (status & SS_READY) return 0;
msleep(unreset_check * 10);
}
dev_err(&skt->dev, "time out after reset\n"); return -ETIMEDOUT;
}
/* * socket_setup() and socket_shutdown() are called by the main event handler * when card insertion and removal events are received. * socket_setup() turns on socket power and resets the socket, in two stages. * socket_shutdown() unconfigures a socket and turns off socket power.
*/ staticvoid socket_shutdown(struct pcmcia_socket *s)
{ int status;
/* Blank out the socket state */
s->socket = dead_socket;
s->ops->init(s);
s->ops->set_socket(s, &s->socket);
s->lock_count = 0;
kfree(s->fake_cis);
s->fake_cis = NULL;
s->functions = 0;
/* From here on we can be sure that only we (that is, the * pccardd thread) accesses this socket, and all (16-bit) * PCMCIA interactions are gone. Therefore, release * ops_mutex so that we don't get a sysfs-related lockdep * warning.
*/
mutex_unlock(&s->ops_mutex);
#ifdef CONFIG_CARDBUS
cb_free(s); #endif
/* give socket some time to power down */
msleep(100);
s->ops->get_status(s, &status); if (status & SS_POWERON) {
dev_err(&s->dev, "*** DANGER *** unable to remove socket power\n");
}
s->state &= ~SOCKET_INUSE;
}
staticint socket_setup(struct pcmcia_socket *skt, int initial_delay)
{ int status, i;
dev_dbg(&skt->dev, "setup\n");
skt->ops->get_status(skt, &status); if (!(status & SS_DETECT)) return -ENODEV;
msleep(initial_delay * 10);
for (i = 0; i < 100; i++) {
skt->ops->get_status(skt, &status); if (!(status & SS_DETECT)) return -ENODEV;
/* * Wait "vcc_settle" for the supply to stabilise.
*/
msleep(vcc_settle * 10);
skt->ops->get_status(skt, &status); if (!(status & SS_POWERON)) {
dev_err(&skt->dev, "unable to apply power\n"); return -EIO;
}
status = socket_reset(skt);
if (skt->power_hook)
skt->power_hook(skt, HOOK_POWER_POST);
return status;
}
/* * Handle card insertion. Setup the socket, reset the card, * and then tell the rest of PCMCIA that a card is present.
*/ staticint socket_insert(struct pcmcia_socket *skt)
{ int ret;
mutex_lock(&skt->ops_mutex); /* store state on first suspend, but not after spurious wakeups */ if (!(skt->state & SOCKET_IN_RESUME))
skt->suspended_state = skt->state;
if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
ret = skt->callback->early_resume(skt); return ret;
}
/* * Finalize the resume. In case of a cardbus socket, we have * to rebind the devices as we can't be certain that it has been * replaced, or not.
*/ staticint socket_complete_resume(struct pcmcia_socket *skt)
{ int ret = 0; #ifdef CONFIG_CARDBUS if (skt->state & SOCKET_CARDBUS) { /* We can't be sure the CardBus card is the same * as the one previously inserted. Therefore, remove
* and re-add... */
cb_free(skt);
ret = cb_alloc(skt); if (ret)
cb_free(skt);
} #endif return ret;
}
/* * Resume a socket. If a card is present, verify its CIS against * our cached copy. If they are different, the card has been * replaced, and we need to tell the drivers.
*/ staticint socket_resume(struct pcmcia_socket *skt)
{ int err; if (!(skt->state & SOCKET_SUSPEND)) return -EBUSY;
/* * Process a socket card detect status change. * * If we don't have a card already present, delay the detect event for * about 20ms (to be on the safe side) before reading the socket status. * * Some i82365-based systems send multiple SS_DETECT events during card * insertion, and the "card present" status bit seems to bounce. This * will probably be true with GPIO-based card detection systems after * the product has aged.
*/ staticvoid socket_detect_change(struct pcmcia_socket *skt)
{ if (!(skt->state & SOCKET_SUSPEND)) { int status;
mutex_lock(&skt->skt_mutex); if (events & SS_DETECT)
socket_detect_change(skt);
if (sysfs_events) { if (sysfs_events & PCMCIA_UEVENT_EJECT)
socket_remove(skt); if (sysfs_events & PCMCIA_UEVENT_INSERT)
socket_insert(skt); if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
!(skt->state & SOCKET_CARDBUS)) { if (skt->callback)
ret = skt->callback->suspend(skt); else
ret = 0; if (!ret) {
socket_suspend(skt);
msleep(100);
}
} if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
!(skt->state & SOCKET_CARDBUS)) {
ret = socket_resume(skt); if (!ret && skt->callback)
skt->callback->resume(skt);
} if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
!(skt->state & SOCKET_CARDBUS)) { if (!ret && skt->callback)
skt->callback->requery(skt);
}
}
mutex_unlock(&skt->skt_mutex);
if (events || sysfs_events) continue;
set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break;
schedule();
try_to_freeze();
} /* make sure we are running before we exit */
__set_current_state(TASK_RUNNING);
/* shut down socket, if a device is still present */ if (skt->state & SOCKET_PRESENT) {
mutex_lock(&skt->skt_mutex);
socket_remove(skt);
mutex_unlock(&skt->skt_mutex);
}
/* remove from the device core */
pccard_sysfs_remove_socket(&skt->dev);
device_unregister(&skt->dev);
return 0;
}
/* * Yenta (at least) probes interrupts before registering the socket and * starting the handler thread.
*/ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
{ unsignedlong flags;
dev_dbg(&s->dev, "parse_events: events %08x\n", events); if (s->thread) {
spin_lock_irqsave(&s->thread_lock, flags);
s->thread_events |= events;
spin_unlock_irqrestore(&s->thread_lock, flags);
/* I'm not sure which "reset" function this is supposed to use, * but for now, it uses the low-level interface's reset, not the * CIS register.
*/
int pcmcia_reset_card(struct pcmcia_socket *skt)
{ int ret;
dev_dbg(&skt->dev, "resetting socket\n");
mutex_lock(&skt->skt_mutex); do { if (!(skt->state & SOCKET_PRESENT)) {
dev_dbg(&skt->dev, "can't reset, not present\n");
ret = -ENODEV; break;
} if (skt->state & SOCKET_SUSPEND) {
dev_dbg(&skt->dev, "can't reset, suspended\n");
ret = -EBUSY; break;
} if (skt->state & SOCKET_CARDBUS) {
dev_dbg(&skt->dev, "can't reset, is cardbus\n");
ret = -EPERM; break;
}
if (skt->callback)
skt->callback->suspend(skt);
mutex_lock(&skt->ops_mutex);
ret = socket_reset(skt);
mutex_unlock(&skt->ops_mutex); if ((ret == 0) && (skt->callback))
skt->callback->resume(skt);
ret = 0;
} while (0);
mutex_unlock(&skt->skt_mutex);
staticconststruct dev_pm_ops pcmcia_socket_pm_ops = { /* dev_resume may be called with IRQs enabled */
SET_SYSTEM_SLEEP_PM_OPS(NULL,
pcmcia_socket_dev_resume)
/* late suspend must be called with IRQs disabled */
.suspend_noirq = pcmcia_socket_dev_suspend_noirq,
.freeze_noirq = pcmcia_socket_dev_suspend_noirq,
.poweroff_noirq = pcmcia_socket_dev_suspend_noirq,
/* early resume must be called with IRQs disabled */
.resume_noirq = pcmcia_socket_dev_resume_noirq,
.thaw_noirq = pcmcia_socket_dev_resume_noirq,
.restore_noirq = pcmcia_socket_dev_resume_noirq,
.complete = pcmcia_socket_dev_complete,
};
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.