summary refs log tree commit diff
path: root/drivers/pcmcia/pcmcia_resource.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pcmcia/pcmcia_resource.c')
-rw-r--r--drivers/pcmcia/pcmcia_resource.c169
1 files changed, 134 insertions, 35 deletions
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index d5db95644b64..b2df04199a21 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -43,6 +43,39 @@ module_param(io_speed, int, 0444);
 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)
+{
+	if (s->resource_ops->adjust_io_region)
+		return s->resource_ops->adjust_io_region(res, start, end, s);
+	return -ENOMEM;
+}
+
+static struct resource *pcmcia_find_io_region(unsigned long base, int num,
+					      unsigned long align,
+					      struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_io)
+		return s->resource_ops->find_io(base, num, align, s);
+	return NULL;
+}
+
+int pcmcia_validate_mem(struct pcmcia_socket *s)
+{
+	if (s->resource_ops->validate_mem)
+		return s->resource_ops->validate_mem(s);
+	/* if there is no callback, we can assume that everything is OK */
+	return 0;
+}
+
+struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
+				 int low, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_mem)
+		return s->resource_ops->find_mem(base, num, align, low, s);
+	return NULL;
+}
+
 
 /** alloc_io_space
  *
@@ -158,14 +191,18 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
 		return -EINVAL;
 
 	s = p_dev->socket;
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 
 	if (!(c->state & CONFIG_LOCKED)) {
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EACCES;
 	}
 
 	addr = (c->ConfigBase + reg->Offset) >> 1;
+	mutex_unlock(&s->ops_mutex);
 
 	switch (reg->Action) {
 	case CS_READ:
@@ -190,6 +227,7 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
 			memreq_t *req)
 {
 	struct pcmcia_socket *s = p_dev->socket;
+	int ret;
 
 	wh--;
 	if (wh >= MAX_WIN)
@@ -198,12 +236,13 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
 		dev_dbg(&s->dev, "failure: requested page is zero\n");
 		return -EINVAL;
 	}
+	mutex_lock(&s->ops_mutex);
 	s->win[wh].card_start = req->CardOffset;
-	if (s->ops->set_mem_map(s, &s->win[wh]) != 0) {
-		dev_dbg(&s->dev, "failed to set_mem_map\n");
-		return -EIO;
-	}
-	return 0;
+	ret = s->ops->set_mem_map(s, &s->win[wh]);
+	if (ret)
+		dev_warn(&s->dev, "failed to set_mem_map\n");
+	mutex_unlock(&s->ops_mutex);
+	return ret;
 } /* pcmcia_map_mem_page */
 EXPORT_SYMBOL(pcmcia_map_mem_page);
 
@@ -219,14 +258,18 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	config_t *c;
 
 	s = p_dev->socket;
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
+		mutex_unlock(&s->ops_mutex);
 		return -ENODEV;
 	}
 	if (!(c->state & CONFIG_LOCKED)) {
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EACCES;
 	}
 
@@ -251,10 +294,12 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	    (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 		if (mod->Vpp1 != mod->Vpp2) {
 			dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n");
+			mutex_unlock(&s->ops_mutex);
 			return -EINVAL;
 		}
 		s->socket.Vpp = mod->Vpp1;
 		if (s->ops->set_socket(s, &s->socket)) {
+			mutex_unlock(&s->ops_mutex);
 			dev_printk(KERN_WARNING, &s->dev,
 				   "Unable to set VPP\n");
 			return -EIO;
@@ -262,6 +307,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
 		   (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 		dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EINVAL;
 	}
 
@@ -286,6 +332,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 			s->ops->set_io_map(s, &io_on);
 		}
 	}
+	mutex_unlock(&s->ops_mutex);
 
 	return 0;
 } /* modify_configuration */
@@ -296,9 +343,11 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 {
 	pccard_io_map io = { 0, 0, 0, 0, 1 };
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	config_t *c;
 	int i;
 
+	mutex_lock(&s->ops_mutex);
+	c = p_dev->function_config;
 	if (p_dev->_locked) {
 		p_dev->_locked = 0;
 		if (--(s->lock_count) == 0) {
@@ -321,6 +370,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 				s->ops->set_io_map(s, &io);
 			}
 	}
+	mutex_unlock(&s->ops_mutex);
 
 	return 0;
 } /* pcmcia_release_configuration */
@@ -337,10 +387,14 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 {
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	int ret = -EINVAL;
+	config_t *c;
+
+	mutex_lock(&s->ops_mutex);
+	c = p_dev->function_config;
 
 	if (!p_dev->_io)
-		return -EINVAL;
+		goto out;
 
 	p_dev->_io = 0;
 
@@ -348,7 +402,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 	    (c->io.NumPorts1 != req->NumPorts1) ||
 	    (c->io.BasePort2 != req->BasePort2) ||
 	    (c->io.NumPorts2 != req->NumPorts2))
-		return -EINVAL;
+		goto out;
 
 	c->state &= ~CONFIG_IO_REQ;
 
@@ -356,28 +410,38 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 	if (req->NumPorts2)
 		release_io_space(s, req->BasePort2, req->NumPorts2);
 
-	return 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_release_io */
 
 
 static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 {
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	config_t *c;
+	int ret = -EINVAL;
+
+	mutex_lock(&s->ops_mutex);
+
+	c = p_dev->function_config;
 
 	if (!p_dev->_irq)
-		return -EINVAL;
+		goto out;
+
 	p_dev->_irq = 0;
 
 	if (c->state & CONFIG_LOCKED)
-		return -EACCES;
+		goto out;
+
 	if (c->irq.Attributes != req->Attributes) {
 		dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
-		return -EINVAL;
+		goto out;
 	}
 	if (s->irq.AssignedIRQ != req->AssignedIRQ) {
 		dev_dbg(&s->dev, "IRQ must match assigned one\n");
-		return -EINVAL;
+		goto out;
 	}
 	if (--s->irq.Config == 0) {
 		c->state &= ~CONFIG_IRQ_REQ;
@@ -390,8 +454,12 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 #ifdef CONFIG_PCMCIA_PROBE
 	pcmcia_used_irq[req->AssignedIRQ]--;
 #endif
+	ret = 0;
 
-	return 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_release_irq */
 
 
@@ -404,10 +472,12 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
 	if (wh >= MAX_WIN)
 		return -EINVAL;
 
+	mutex_lock(&s->ops_mutex);
 	win = &s->win[wh];
 
 	if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) {
 		dev_dbg(&s->dev, "not releasing unknown window\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EINVAL;
 	}
 
@@ -423,6 +493,7 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
 		win->res = NULL;
 	}
 	p_dev->_win &= ~CLIENT_WIN_REQ(wh);
+	mutex_unlock(&s->ops_mutex);
 
 	return 0;
 } /* pcmcia_release_window */
@@ -445,8 +516,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 		dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n");
 		return -EINVAL;
 	}
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
+		mutex_unlock(&s->ops_mutex);
 		dev_dbg(&s->dev, "Configuration is locked\n");
 		return -EACCES;
 	}
@@ -454,6 +528,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 	/* Do power control.  We don't allow changes in Vcc. */
 	s->socket.Vpp = req->Vpp;
 	if (s->ops->set_socket(s, &s->socket)) {
+		mutex_unlock(&s->ops_mutex);
 		dev_printk(KERN_WARNING, &s->dev,
 			   "Unable to set socket state\n");
 		return -EINVAL;
@@ -476,6 +551,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 		s->socket.io_irq = 0;
 	s->ops->set_socket(s, &s->socket);
 	s->lock_count++;
+	mutex_unlock(&s->ops_mutex);
 
 	/* Set up CIS configuration registers */
 	base = c->ConfigBase = req->ConfigBase;
@@ -524,6 +600,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 
 	/* Configure I/O windows */
 	if (c->state & CONFIG_IO_REQ) {
+		mutex_lock(&s->ops_mutex);
 		iomap.speed = io_speed;
 		for (i = 0; i < MAX_IO_WIN; i++)
 			if (s->io[i].res) {
@@ -542,6 +619,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 				s->ops->set_io_map(s, &iomap);
 				s->io[i].Config++;
 			}
+		mutex_unlock(&s->ops_mutex);
 	}
 
 	c->state |= CONFIG_LOCKED;
@@ -560,54 +638,65 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
 {
 	struct pcmcia_socket *s = p_dev->socket;
 	config_t *c;
+	int ret = -EINVAL;
+
+	mutex_lock(&s->ops_mutex);
 
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
-		return -ENODEV;
+		goto out;
 	}
 
 	if (!req)
-		return -EINVAL;
+		goto out;
+
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
 		dev_dbg(&s->dev, "Configuration is locked\n");
-		return -EACCES;
+		goto out;
 	}
 	if (c->state & CONFIG_IO_REQ) {
 		dev_dbg(&s->dev, "IO already configured\n");
-		return -EBUSY;
+		goto out;
 	}
 	if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
 		dev_dbg(&s->dev, "bad attribute setting for IO region 1\n");
-		return -EINVAL;
+		goto out;
 	}
 	if ((req->NumPorts2 > 0) &&
 	    (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
 		dev_dbg(&s->dev, "bad attribute setting for IO region 2\n");
-		return -EINVAL;
+		goto out;
 	}
 
 	dev_dbg(&s->dev, "trying to allocate resource 1\n");
-	if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
-			   req->NumPorts1, req->IOAddrLines)) {
+	ret = alloc_io_space(s, req->Attributes1, &req->BasePort1,
+			     req->NumPorts1, req->IOAddrLines);
+	if (ret) {
 		dev_dbg(&s->dev, "allocation of resource 1 failed\n");
-		return -EBUSY;
+		goto out;
 	}
 
 	if (req->NumPorts2) {
 		dev_dbg(&s->dev, "trying to allocate resource 2\n");
-		if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
-				   req->NumPorts2, req->IOAddrLines)) {
+		ret = alloc_io_space(s, req->Attributes2, &req->BasePort2,
+				     req->NumPorts2, req->IOAddrLines);
+		if (ret) {
 			dev_dbg(&s->dev, "allocation of resource 2 failed\n");
 			release_io_space(s, req->BasePort1, req->NumPorts1);
-			return -EBUSY;
+			goto out;
 		}
 	}
 
 	c->io = *req;
 	c->state |= CONFIG_IO_REQ;
 	p_dev->_io = 1;
-	return 0;
+	dev_dbg(&s->dev, "allocating resources succeeded: %d\n", ret);
+
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_request_io */
 EXPORT_SYMBOL(pcmcia_request_io);
 
@@ -636,18 +725,20 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	int ret = -EINVAL, irq = 0;
 	int type;
 
+	mutex_lock(&s->ops_mutex);
+
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
-		return -ENODEV;
+		goto out;
 	}
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
 		dev_dbg(&s->dev, "Configuration is locked\n");
-		return -EACCES;
+		goto out;
 	}
 	if (c->state & CONFIG_IRQ_REQ) {
 		dev_dbg(&s->dev, "IRQ already configured\n");
-		return -EBUSY;
+		goto out;
 	}
 
 	/* Decide what type of interrupt we are registering */
@@ -708,7 +799,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	if (ret && !s->irq.AssignedIRQ) {
 		if (!s->pci_irq) {
 			dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
-			return ret;
+			goto out;
 		}
 		type = IRQF_SHARED;
 		irq = s->pci_irq;
@@ -720,7 +811,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 		if (ret) {
 			dev_printk(KERN_INFO, &s->dev,
 				"request_irq() failed\n");
-			return ret;
+			goto out;
 		}
 	}
 
@@ -743,7 +834,10 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	pcmcia_used_irq[irq]++;
 #endif
 
-	return 0;
+	ret = 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+	return ret;
 } /* pcmcia_request_irq */
 EXPORT_SYMBOL(pcmcia_request_irq);
 
@@ -796,6 +890,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 		return -EINVAL;
 	}
 
+	mutex_lock(&s->ops_mutex);
 	win = &s->win[w];
 
 	if (!(s->features & SS_CAP_STATIC_MAP)) {
@@ -803,6 +898,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 						      (req->Attributes & WIN_MAP_BELOW_1MB), s);
 		if (!win->res) {
 			dev_dbg(&s->dev, "allocating mem region failed\n");
+			mutex_unlock(&s->ops_mutex);
 			return -EINVAL;
 		}
 	}
@@ -821,8 +917,10 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 	if (req->Attributes & WIN_USE_WAIT)
 		win->flags |= MAP_USE_WAIT;
 	win->card_start = 0;
+
 	if (s->ops->set_mem_map(s, win) != 0) {
 		dev_dbg(&s->dev, "failed to set memory mapping\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EIO;
 	}
 	s->state |= SOCKET_WIN_REQ(w);
@@ -833,6 +931,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 	else
 		req->Base = win->res->start;
 
+	mutex_unlock(&s->ops_mutex);
 	*wh = w + 1;
 
 	return 0;