Skip to content

Commit

Permalink
pcmcia: setup IRQ to be used by PCMCIA drivers at card insert
Browse files Browse the repository at this point in the history
Setup the IRQ to be used by PCMCIA drivers already during the device
registration stage, making use of a new function pcmcia_setup_irq().
This will allow us to get rid of quite a lot of indirection in the
future.

Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
  • Loading branch information
Dominik Brodowski committed May 10, 2010
1 parent 0cb3c49 commit 6f0f38c
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 94 deletions.
2 changes: 2 additions & 0 deletions drivers/pcmcia/cs_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ extern struct resource *pcmcia_find_mem_region(u_long base,
int low,
struct pcmcia_socket *s);

void pcmcia_cleanup_irq(struct pcmcia_socket *s);
int pcmcia_setup_irq(struct pcmcia_device *p_dev);

/* cistpl.c */
extern struct bin_attribute pccard_cis_attr;
Expand Down
15 changes: 11 additions & 4 deletions drivers/pcmcia/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,26 +546,32 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
p_dev->function_config = tmp_dev->function_config;
p_dev->io = tmp_dev->io;
p_dev->irq = tmp_dev->irq;
p_dev->irq_v = tmp_dev->irq_v;
kref_get(&p_dev->function_config->ref);
}

/* Add to the list in pcmcia_bus_socket */
list_add(&p_dev->socket_device_list, &s->devices_list);

mutex_unlock(&s->ops_mutex);
if (pcmcia_setup_irq(p_dev))
dev_warn(&p_dev->dev,
"IRQ setup failed -- device might not work\n");

if (!p_dev->function_config) {
dev_dbg(&p_dev->dev, "creating config_t\n");
p_dev->function_config = kzalloc(sizeof(struct config_t),
GFP_KERNEL);
if (!p_dev->function_config)
if (!p_dev->function_config) {
mutex_unlock(&s->ops_mutex);
goto err_unreg;
}
kref_init(&p_dev->function_config->ref);
}
mutex_unlock(&s->ops_mutex);

dev_printk(KERN_NOTICE, &p_dev->dev,
"pcmcia: registering new device %s\n",
p_dev->devname);
"pcmcia: registering new device %s (IRQ: %d)\n",
p_dev->devname, p_dev->irq_v);

pcmcia_device_query(p_dev);

Expand Down Expand Up @@ -1258,6 +1264,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
handle_event(skt, event);
mutex_lock(&s->ops_mutex);
destroy_cis_cache(s);
pcmcia_cleanup_irq(s);
mutex_unlock(&s->ops_mutex);
break;

Expand Down
214 changes: 124 additions & 90 deletions drivers/pcmcia/pcmcia_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <linux/netdevice.h>
#include <linux/slab.h>

#include <asm/irq.h>

#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
Expand All @@ -38,12 +40,6 @@ static int io_speed;
module_param(io_speed, int, 0444);


#ifdef CONFIG_PCMCIA_PROBE
#include <asm/irq.h>
/* mask of IRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];
#endif

static int pcmcia_adjust_io_region(struct resource *res, unsigned long start,
unsigned long end, struct pcmcia_socket *s)
{
Expand Down Expand Up @@ -440,15 +436,11 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
}
if (--s->irq.Config == 0) {
c->state &= ~CONFIG_IRQ_REQ;
s->irq.AssignedIRQ = 0;
}

if (req->Handler)
free_irq(req->AssignedIRQ, p_dev->priv);

#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[req->AssignedIRQ]--;
#endif
ret = 0;

out:
Expand Down Expand Up @@ -699,26 +691,14 @@ EXPORT_SYMBOL(pcmcia_request_io);
/** pcmcia_request_irq
*
* Request_irq() reserves an irq for this client.
*
* Also, since Linux only reserves irq's when they are actually
* hooked, we don't guarantee that an irq will still be available
* when the configuration is locked. Now that I think about it,
* there might be a way to fix this using a dummy handler.
*/

#ifdef CONFIG_PCMCIA_PROBE
static irqreturn_t test_action(int cpl, void *dev_id)
{
return IRQ_NONE;
}
#endif

int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
{
struct pcmcia_socket *s = p_dev->socket;
config_t *c;
int ret = -EINVAL, irq = 0;
int type;
int ret = -EINVAL, irq = p_dev->irq_v;
int type = IRQF_SHARED;

mutex_lock(&s->ops_mutex);

Expand All @@ -736,63 +716,20 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
goto out;
}

/* Decide what type of interrupt we are registering */
type = 0;
if (s->functions > 1) /* All of this ought to be handled higher up */
type = IRQF_SHARED;
else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)
type = IRQF_SHARED;
else
printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n");

/* If the interrupt is already assigned, it must be the same */
if (s->irq.AssignedIRQ != 0)
irq = s->irq.AssignedIRQ;

#ifdef CONFIG_PCMCIA_PROBE
if (!irq) {
int try;
u32 mask = s->irq_mask;
void *data = p_dev; /* something unique to this device */

for (try = 0; try < 64; try++) {
irq = try % 32;

/* marked as available by driver, and not blocked by userspace? */
if (!((mask >> irq) & 1))
continue;

/* avoid an IRQ which is already used by a PCMCIA card */
if ((try < 32) && pcmcia_used_irq[irq])
continue;

/* register the correct driver, if possible, of check whether
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq,
(req->Handler) ? req->Handler : test_action,
type,
p_dev->devname,
(req->Handler) ? p_dev->priv : data);
if (!ret) {
if (!req->Handler)
free_irq(irq, data);
break;
}
}
dev_dbg(&s->dev, "no IRQ available\n");
goto out;
}
#endif
/* only assign PCI irq if no IRQ already assigned */
if (ret && !s->irq.AssignedIRQ) {
if (!s->pci_irq) {
dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
goto out;
}
type = IRQF_SHARED;
irq = s->pci_irq;

if (!(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) {
req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING;
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
"request for exclusive IRQ could not be fulfilled.\n");
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
"needs updating to supported shared IRQ lines.\n");
}

if (ret && req->Handler) {
if (req->Handler) {
ret = request_irq(irq, req->Handler, type,
p_dev->devname, p_dev->priv);
if (ret) {
Expand All @@ -802,25 +739,13 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
}
}

/* Make sure the fact the request type was overridden is passed back */
if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) {
req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING;
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
"request for exclusive IRQ could not be fulfilled.\n");
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
"needs updating to supported shared IRQ lines.\n");
}
c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
req->AssignedIRQ = irq;
s->irq.Config++;

c->state |= CONFIG_IRQ_REQ;
p_dev->_irq = 1;

#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[irq]++;
#endif

ret = 0;
out:
mutex_unlock(&s->ops_mutex);
Expand All @@ -829,6 +754,115 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
EXPORT_SYMBOL(pcmcia_request_irq);


#ifdef CONFIG_PCMCIA_PROBE

/* mask of IRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];

static irqreturn_t test_action(int cpl, void *dev_id)
{
return IRQ_NONE;
}

/**
* pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used
* @p_dev - the associated PCMCIA device
*
* locking note: must be called with ops_mutex locked.
*/
static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
{
struct pcmcia_socket *s = p_dev->socket;
unsigned int try, irq;
u32 mask = s->irq_mask;
int ret = -ENODEV;

for (try = 0; try < 64; try++) {
irq = try % 32;

/* marked as available by driver, not blocked by userspace? */
if (!((mask >> irq) & 1))
continue;

/* avoid an IRQ which is already used by another PCMCIA card */
if ((try < 32) && pcmcia_used_irq[irq])
continue;

/* register the correct driver, if possible, to check whether
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq, test_action, type, p_dev->devname,
p_dev);
if (!ret) {
free_irq(irq, p_dev);
p_dev->irq_v = s->irq.AssignedIRQ = irq;
pcmcia_used_irq[irq]++;
break;
}
}

return ret;
}

void pcmcia_cleanup_irq(struct pcmcia_socket *s)
{
pcmcia_used_irq[s->irq.AssignedIRQ]--;
s->irq.AssignedIRQ = 0;
}

#else /* CONFIG_PCMCIA_PROBE */

static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
{
return -EINVAL;
}

void pcmcia_cleanup_irq(struct pcmcia_socket *s)
{
s->irq.AssignedIRQ = 0;
return;
}

#endif /* CONFIG_PCMCIA_PROBE */


/**
* pcmcia_setup_irq() - determine IRQ to be used for device
* @p_dev - the associated PCMCIA device
*
* locking note: must be called with ops_mutex locked.
*/
int pcmcia_setup_irq(struct pcmcia_device *p_dev)
{
struct pcmcia_socket *s = p_dev->socket;

if (p_dev->irq_v)
return 0;

/* already assigned? */
if (s->irq.AssignedIRQ) {
p_dev->irq_v = s->irq.AssignedIRQ;
return 0;
}

/* prefer an exclusive ISA irq */
if (!pcmcia_setup_isa_irq(p_dev, 0))
return 0;

/* but accept a shared ISA irq */
if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED))
return 0;

/* but use the PCI irq otherwise */
if (s->pci_irq) {
p_dev->irq_v = s->irq.AssignedIRQ = s->pci_irq;
return 0;
}

return -EINVAL;
}


/** pcmcia_request_window
*
* Request_window() establishes a mapping between card memory space
Expand Down
3 changes: 3 additions & 0 deletions include/pcmcia/ds.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ struct pcmcia_device {
config_req_t conf;
window_handle_t win;

/* device setup */
unsigned int irq_v; /* do not use directly yet */

/* Is the device suspended? */
u16 suspended:1;

Expand Down

0 comments on commit 6f0f38c

Please sign in to comment.