|
@@ -33,6 +33,7 @@
|
|
|
|
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio.h>
|
|
|
|
+#include <linux/gpio/consumer.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/io.h>
|
|
@@ -42,6 +43,7 @@
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/mutex.h>
|
|
|
|
+#include <linux/regulator/consumer.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/timer.h>
|
|
|
|
|
|
@@ -79,6 +81,41 @@ EXPORT_SYMBOL(soc_pcmcia_debug);
|
|
#define to_soc_pcmcia_socket(x) \
|
|
#define to_soc_pcmcia_socket(x) \
|
|
container_of(x, struct soc_pcmcia_socket, socket)
|
|
container_of(x, struct soc_pcmcia_socket, socket)
|
|
|
|
|
|
|
|
+int soc_pcmcia_regulator_set(struct soc_pcmcia_socket *skt,
|
|
|
|
+ struct soc_pcmcia_regulator *r, int v)
|
|
|
|
+{
|
|
|
|
+ bool on;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!r->reg)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ on = v != 0;
|
|
|
|
+ if (r->on == on)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (on) {
|
|
|
|
+ ret = regulator_set_voltage(r->reg, v * 100000, v * 100000);
|
|
|
|
+ if (ret) {
|
|
|
|
+ int vout = regulator_get_voltage(r->reg) / 100000;
|
|
|
|
+
|
|
|
|
+ dev_warn(&skt->socket.dev,
|
|
|
|
+ "CS requested %s=%u.%uV, applying %u.%uV\n",
|
|
|
|
+ r == &skt->vcc ? "Vcc" : "Vpp",
|
|
|
|
+ v / 10, v % 10, vout / 10, vout % 10);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = regulator_enable(r->reg);
|
|
|
|
+ } else {
|
|
|
|
+ regulator_disable(r->reg);
|
|
|
|
+ }
|
|
|
|
+ if (ret == 0)
|
|
|
|
+ r->on = on;
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(soc_pcmcia_regulator_set);
|
|
|
|
+
|
|
static unsigned short
|
|
static unsigned short
|
|
calc_speed(unsigned short *spds, int num, unsigned short dflt)
|
|
calc_speed(unsigned short *spds, int num, unsigned short dflt)
|
|
{
|
|
{
|
|
@@ -111,12 +148,9 @@ static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt,
|
|
{
|
|
{
|
|
unsigned int i;
|
|
unsigned int i;
|
|
|
|
|
|
- for (i = 0; i < nr; i++) {
|
|
|
|
|
|
+ for (i = 0; i < nr; i++)
|
|
if (skt->stat[i].irq)
|
|
if (skt->stat[i].irq)
|
|
free_irq(skt->stat[i].irq, skt);
|
|
free_irq(skt->stat[i].irq, skt);
|
|
- if (gpio_is_valid(skt->stat[i].gpio))
|
|
|
|
- gpio_free(skt->stat[i].gpio);
|
|
|
|
- }
|
|
|
|
|
|
|
|
if (skt->ops->hw_shutdown)
|
|
if (skt->ops->hw_shutdown)
|
|
skt->ops->hw_shutdown(skt);
|
|
skt->ops->hw_shutdown(skt);
|
|
@@ -129,6 +163,30 @@ static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
|
|
__soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat));
|
|
__soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int soc_pcmcia_request_gpiods(struct soc_pcmcia_socket *skt)
|
|
|
|
+{
|
|
|
|
+ struct device *dev = skt->socket.dev.parent;
|
|
|
|
+ struct gpio_desc *desc;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(skt->stat); i++) {
|
|
|
|
+ if (!skt->stat[i].name)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ desc = devm_gpiod_get(dev, skt->stat[i].name, GPIOD_IN);
|
|
|
|
+ if (IS_ERR(desc)) {
|
|
|
|
+ dev_err(dev, "Failed to get GPIO for %s: %ld\n",
|
|
|
|
+ skt->stat[i].name, PTR_ERR(desc));
|
|
|
|
+ return PTR_ERR(desc);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ skt->stat[i].desc = desc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(soc_pcmcia_request_gpiods);
|
|
|
|
+
|
|
static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
|
|
static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
|
|
{
|
|
{
|
|
int ret = 0, i;
|
|
int ret = 0, i;
|
|
@@ -143,21 +201,32 @@ static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(skt->stat); i++) {
|
|
for (i = 0; i < ARRAY_SIZE(skt->stat); i++) {
|
|
if (gpio_is_valid(skt->stat[i].gpio)) {
|
|
if (gpio_is_valid(skt->stat[i].gpio)) {
|
|
- int irq;
|
|
|
|
|
|
+ unsigned long flags = GPIOF_IN;
|
|
|
|
|
|
- ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN,
|
|
|
|
- skt->stat[i].name);
|
|
|
|
|
|
+ /* CD is active low by default */
|
|
|
|
+ if (i == SOC_STAT_CD)
|
|
|
|
+ flags |= GPIOF_ACTIVE_LOW;
|
|
|
|
+
|
|
|
|
+ ret = devm_gpio_request_one(skt->socket.dev.parent,
|
|
|
|
+ skt->stat[i].gpio, flags,
|
|
|
|
+ skt->stat[i].name);
|
|
if (ret) {
|
|
if (ret) {
|
|
__soc_pcmcia_hw_shutdown(skt, i);
|
|
__soc_pcmcia_hw_shutdown(skt, i);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
- irq = gpio_to_irq(skt->stat[i].gpio);
|
|
|
|
|
|
+ skt->stat[i].desc = gpio_to_desc(skt->stat[i].gpio);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i < SOC_STAT_VS1 && skt->stat[i].desc) {
|
|
|
|
+ int irq = gpiod_to_irq(skt->stat[i].desc);
|
|
|
|
|
|
- if (i == SOC_STAT_RDY)
|
|
|
|
- skt->socket.pci_irq = irq;
|
|
|
|
- else
|
|
|
|
- skt->stat[i].irq = irq;
|
|
|
|
|
|
+ if (irq > 0) {
|
|
|
|
+ if (i == SOC_STAT_RDY)
|
|
|
|
+ skt->socket.pci_irq = irq;
|
|
|
|
+ else
|
|
|
|
+ skt->stat[i].irq = irq;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (skt->stat[i].irq) {
|
|
if (skt->stat[i].irq) {
|
|
@@ -166,8 +235,6 @@ static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
|
|
IRQF_TRIGGER_NONE,
|
|
IRQF_TRIGGER_NONE,
|
|
skt->stat[i].name, skt);
|
|
skt->stat[i].name, skt);
|
|
if (ret) {
|
|
if (ret) {
|
|
- if (gpio_is_valid(skt->stat[i].gpio))
|
|
|
|
- gpio_free(skt->stat[i].gpio);
|
|
|
|
__soc_pcmcia_hw_shutdown(skt, i);
|
|
__soc_pcmcia_hw_shutdown(skt, i);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -197,6 +264,18 @@ static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt)
|
|
irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE);
|
|
irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * The CF 3.0 specification says that cards tie VS1 to ground and leave
|
|
|
|
+ * VS2 open. Many implementations do not wire up the VS signals, so we
|
|
|
|
+ * provide hard-coded values as per the CF 3.0 spec.
|
|
|
|
+ */
|
|
|
|
+void soc_common_cf_socket_state(struct soc_pcmcia_socket *skt,
|
|
|
|
+ struct pcmcia_state *state)
|
|
|
|
+{
|
|
|
|
+ state->vs_3v = 1;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(soc_common_cf_socket_state);
|
|
|
|
+
|
|
static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
|
|
static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
|
|
{
|
|
{
|
|
struct pcmcia_state state;
|
|
struct pcmcia_state state;
|
|
@@ -208,17 +287,18 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
|
|
state.bvd1 = 1;
|
|
state.bvd1 = 1;
|
|
state.bvd2 = 1;
|
|
state.bvd2 = 1;
|
|
|
|
|
|
- /* CD is active low by default */
|
|
|
|
- if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio))
|
|
|
|
- state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio);
|
|
|
|
-
|
|
|
|
- /* RDY and BVD are active high by default */
|
|
|
|
- if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio))
|
|
|
|
- state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio);
|
|
|
|
- if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio))
|
|
|
|
- state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio);
|
|
|
|
- if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio))
|
|
|
|
- state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio);
|
|
|
|
|
|
+ if (skt->stat[SOC_STAT_CD].desc)
|
|
|
|
+ state.detect = !!gpiod_get_value(skt->stat[SOC_STAT_CD].desc);
|
|
|
|
+ if (skt->stat[SOC_STAT_RDY].desc)
|
|
|
|
+ state.ready = !!gpiod_get_value(skt->stat[SOC_STAT_RDY].desc);
|
|
|
|
+ if (skt->stat[SOC_STAT_BVD1].desc)
|
|
|
|
+ state.bvd1 = !!gpiod_get_value(skt->stat[SOC_STAT_BVD1].desc);
|
|
|
|
+ if (skt->stat[SOC_STAT_BVD2].desc)
|
|
|
|
+ state.bvd2 = !!gpiod_get_value(skt->stat[SOC_STAT_BVD2].desc);
|
|
|
|
+ if (skt->stat[SOC_STAT_VS1].desc)
|
|
|
|
+ state.vs_3v = !!gpiod_get_value(skt->stat[SOC_STAT_VS1].desc);
|
|
|
|
+ if (skt->stat[SOC_STAT_VS2].desc)
|
|
|
|
+ state.vs_Xv = !!gpiod_get_value(skt->stat[SOC_STAT_VS2].desc);
|
|
|
|
|
|
skt->ops->socket_state(skt, &state);
|
|
skt->ops->socket_state(skt, &state);
|
|
|
|
|
|
@@ -257,7 +337,30 @@ static int soc_common_pcmcia_config_skt(
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
ret = skt->ops->configure_socket(skt, state);
|
|
ret = skt->ops->configure_socket(skt, state);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ pr_err("soc_common_pcmcia: unable to configure socket %d\n",
|
|
|
|
+ skt->nr);
|
|
|
|
+ /* restore the previous state */
|
|
|
|
+ WARN_ON(skt->ops->configure_socket(skt, &skt->cs_state));
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (ret == 0) {
|
|
if (ret == 0) {
|
|
|
|
+ struct gpio_desc *descs[2];
|
|
|
|
+ int values[2], n = 0;
|
|
|
|
+
|
|
|
|
+ if (skt->gpio_reset) {
|
|
|
|
+ descs[n] = skt->gpio_reset;
|
|
|
|
+ values[n++] = !!(state->flags & SS_RESET);
|
|
|
|
+ }
|
|
|
|
+ if (skt->gpio_bus_enable) {
|
|
|
|
+ descs[n] = skt->gpio_bus_enable;
|
|
|
|
+ values[n++] = !!(state->flags & SS_OUTPUT_ENA);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (n)
|
|
|
|
+ gpiod_set_array_value_cansleep(n, descs, values);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This really needs a better solution. The IRQ
|
|
* This really needs a better solution. The IRQ
|
|
* may or may not be claimed by the driver.
|
|
* may or may not be claimed by the driver.
|
|
@@ -274,10 +377,6 @@ static int soc_common_pcmcia_config_skt(
|
|
skt->cs_state = *state;
|
|
skt->cs_state = *state;
|
|
}
|
|
}
|
|
|
|
|
|
- if (ret < 0)
|
|
|
|
- printk(KERN_ERR "soc_common_pcmcia: unable to configure "
|
|
|
|
- "socket %d\n", skt->nr);
|
|
|
|
-
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -637,54 +736,19 @@ static struct pccard_operations soc_common_pcmcia_operations = {
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
-static LIST_HEAD(soc_pcmcia_sockets);
|
|
|
|
-static DEFINE_MUTEX(soc_pcmcia_sockets_lock);
|
|
|
|
-
|
|
|
|
#ifdef CONFIG_CPU_FREQ
|
|
#ifdef CONFIG_CPU_FREQ
|
|
-static int
|
|
|
|
-soc_pcmcia_notifier(struct notifier_block *nb, unsigned long val, void *data)
|
|
|
|
|
|
+static int soc_common_pcmcia_cpufreq_nb(struct notifier_block *nb,
|
|
|
|
+ unsigned long val, void *data)
|
|
{
|
|
{
|
|
- struct soc_pcmcia_socket *skt;
|
|
|
|
|
|
+ struct soc_pcmcia_socket *skt = container_of(nb, struct soc_pcmcia_socket, cpufreq_nb);
|
|
struct cpufreq_freqs *freqs = data;
|
|
struct cpufreq_freqs *freqs = data;
|
|
- int ret = 0;
|
|
|
|
-
|
|
|
|
- mutex_lock(&soc_pcmcia_sockets_lock);
|
|
|
|
- list_for_each_entry(skt, &soc_pcmcia_sockets, node)
|
|
|
|
- if (skt->ops->frequency_change)
|
|
|
|
- ret += skt->ops->frequency_change(skt, val, freqs);
|
|
|
|
- mutex_unlock(&soc_pcmcia_sockets_lock);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static struct notifier_block soc_pcmcia_notifier_block = {
|
|
|
|
- .notifier_call = soc_pcmcia_notifier
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static int soc_pcmcia_cpufreq_register(void)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
|
|
|
|
- ret = cpufreq_register_notifier(&soc_pcmcia_notifier_block,
|
|
|
|
- CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
- if (ret < 0)
|
|
|
|
- printk(KERN_ERR "Unable to register CPU frequency change "
|
|
|
|
- "notifier for PCMCIA (%d)\n", ret);
|
|
|
|
- return ret;
|
|
|
|
|
|
+ return skt->ops->frequency_change(skt, val, freqs);
|
|
}
|
|
}
|
|
-fs_initcall(soc_pcmcia_cpufreq_register);
|
|
|
|
-
|
|
|
|
-static void soc_pcmcia_cpufreq_unregister(void)
|
|
|
|
-{
|
|
|
|
- cpufreq_unregister_notifier(&soc_pcmcia_notifier_block,
|
|
|
|
- CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
-}
|
|
|
|
-module_exit(soc_pcmcia_cpufreq_unregister);
|
|
|
|
-
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
|
|
void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
|
|
- struct pcmcia_low_level *ops, struct device *dev)
|
|
|
|
|
|
+ const struct pcmcia_low_level *ops, struct device *dev)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
|
|
@@ -700,19 +764,21 @@ EXPORT_SYMBOL(soc_pcmcia_init_one);
|
|
|
|
|
|
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
|
|
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
|
|
{
|
|
{
|
|
- mutex_lock(&soc_pcmcia_sockets_lock);
|
|
|
|
del_timer_sync(&skt->poll_timer);
|
|
del_timer_sync(&skt->poll_timer);
|
|
|
|
|
|
pcmcia_unregister_socket(&skt->socket);
|
|
pcmcia_unregister_socket(&skt->socket);
|
|
|
|
|
|
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
|
|
+ if (skt->ops->frequency_change)
|
|
|
|
+ cpufreq_unregister_notifier(&skt->cpufreq_nb,
|
|
|
|
+ CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
+#endif
|
|
|
|
+
|
|
soc_pcmcia_hw_shutdown(skt);
|
|
soc_pcmcia_hw_shutdown(skt);
|
|
|
|
|
|
/* should not be required; violates some lowlevel drivers */
|
|
/* should not be required; violates some lowlevel drivers */
|
|
soc_common_pcmcia_config_skt(skt, &dead_socket);
|
|
soc_common_pcmcia_config_skt(skt, &dead_socket);
|
|
|
|
|
|
- list_del(&skt->node);
|
|
|
|
- mutex_unlock(&soc_pcmcia_sockets_lock);
|
|
|
|
-
|
|
|
|
iounmap(skt->virt_io);
|
|
iounmap(skt->virt_io);
|
|
skt->virt_io = NULL;
|
|
skt->virt_io = NULL;
|
|
release_resource(&skt->res_attr);
|
|
release_resource(&skt->res_attr);
|
|
@@ -726,6 +792,8 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
+ skt->cs_state = dead_socket;
|
|
|
|
+
|
|
setup_timer(&skt->poll_timer, soc_common_pcmcia_poll_event,
|
|
setup_timer(&skt->poll_timer, soc_common_pcmcia_poll_event,
|
|
(unsigned long)skt);
|
|
(unsigned long)skt);
|
|
skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
|
|
skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
|
|
@@ -752,10 +820,6 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
|
|
goto out_err_5;
|
|
goto out_err_5;
|
|
}
|
|
}
|
|
|
|
|
|
- mutex_lock(&soc_pcmcia_sockets_lock);
|
|
|
|
-
|
|
|
|
- list_add(&skt->node, &soc_pcmcia_sockets);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* We initialize default socket timing here, because
|
|
* We initialize default socket timing here, because
|
|
* we are not guaranteed to see a SetIOMap operation at
|
|
* we are not guaranteed to see a SetIOMap operation at
|
|
@@ -776,14 +840,23 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
|
|
|
|
|
|
skt->status = soc_common_pcmcia_skt_state(skt);
|
|
skt->status = soc_common_pcmcia_skt_state(skt);
|
|
|
|
|
|
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
|
|
+ if (skt->ops->frequency_change) {
|
|
|
|
+ skt->cpufreq_nb.notifier_call = soc_common_pcmcia_cpufreq_nb;
|
|
|
|
+
|
|
|
|
+ ret = cpufreq_register_notifier(&skt->cpufreq_nb,
|
|
|
|
+ CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_err(skt->socket.dev.parent,
|
|
|
|
+ "unable to register CPU frequency change notifier for PCMCIA (%d)\n",
|
|
|
|
+ ret);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
ret = pcmcia_register_socket(&skt->socket);
|
|
ret = pcmcia_register_socket(&skt->socket);
|
|
if (ret)
|
|
if (ret)
|
|
goto out_err_7;
|
|
goto out_err_7;
|
|
|
|
|
|
- add_timer(&skt->poll_timer);
|
|
|
|
-
|
|
|
|
- mutex_unlock(&soc_pcmcia_sockets_lock);
|
|
|
|
-
|
|
|
|
ret = device_create_file(&skt->socket.dev, &dev_attr_status);
|
|
ret = device_create_file(&skt->socket.dev, &dev_attr_status);
|
|
if (ret)
|
|
if (ret)
|
|
goto out_err_8;
|
|
goto out_err_8;
|
|
@@ -791,15 +864,12 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
out_err_8:
|
|
out_err_8:
|
|
- mutex_lock(&soc_pcmcia_sockets_lock);
|
|
|
|
del_timer_sync(&skt->poll_timer);
|
|
del_timer_sync(&skt->poll_timer);
|
|
pcmcia_unregister_socket(&skt->socket);
|
|
pcmcia_unregister_socket(&skt->socket);
|
|
|
|
|
|
out_err_7:
|
|
out_err_7:
|
|
soc_pcmcia_hw_shutdown(skt);
|
|
soc_pcmcia_hw_shutdown(skt);
|
|
out_err_6:
|
|
out_err_6:
|
|
- list_del(&skt->node);
|
|
|
|
- mutex_unlock(&soc_pcmcia_sockets_lock);
|
|
|
|
iounmap(skt->virt_io);
|
|
iounmap(skt->virt_io);
|
|
out_err_5:
|
|
out_err_5:
|
|
release_resource(&skt->res_attr);
|
|
release_resource(&skt->res_attr);
|