summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-10 11:34:28 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-10 11:34:28 -0700
commit911f9dab301e8583143c7e75b552eadd434ea0a8 (patch)
tree5ae66958da8ad33ac556d2cf59ce915edddec5ad
parentae50a840e779d48aa4dea2aaec1778f6b3f1d67f (diff)
parent4a5d67d7346619f4a45b5d6f7a496db2e0e8545d (diff)
downloadlinux-911f9dab301e8583143c7e75b552eadd434ea0a8.tar.bz2
Merge branch 'pcmcia' of git://git.armlinux.org.uk/~rmk/linux-arm
Pull ARM pcmcia updates from Russell King: "These updates lay the foundations for more generic soc_common PCMCIA support, which will result in several of the board specific drivers being elimated. As the dependencies for this are complex, the preliminary work is being submitted now, with the remainder scheduled for the next merge window" * 'pcmcia' of git://git.armlinux.org.uk/~rmk/linux-arm: pcmcia: soc_common: add driver-data pointer pcmcia: soc_common: add support for voltage sense GPIOs pcmcia: soc_common: constify pcmcia_low_level ops pointer pcmcia: soc_common: switch to a per-socket cpufreq notifier pcmcia: soc_common: add support for Vcc and Vpp regulators pcmcia: soc_common: add CF socket state helper pcmcia: soc_common: restore previous socket state on error pcmcia: soc_common: add support for reset and bus enable GPIOs pcmcia: soc_common: request legacy detect GPIO with active low pcmcia: soc_common: ignore invalid interrupts pcmcia: soc_common: switch to using gpio_descs pcmcia: soc_common: use devm_gpio_request_one()
-rw-r--r--drivers/pcmcia/sa1100_assabet.c9
-rw-r--r--drivers/pcmcia/sa1100_cerf.c9
-rw-r--r--drivers/pcmcia/soc_common.c238
-rw-r--r--drivers/pcmcia/soc_common.h29
4 files changed, 183 insertions, 102 deletions
diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c
index 44cfc4416e54..71ace6910d7e 100644
--- a/drivers/pcmcia/sa1100_assabet.c
+++ b/drivers/pcmcia/sa1100_assabet.c
@@ -31,13 +31,6 @@ static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return 0;
}
-static void
-assabet_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
-{
- state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */
- state->vs_Xv = 0;
-}
-
static int
assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
@@ -90,7 +83,7 @@ static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
static struct pcmcia_low_level assabet_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = assabet_pcmcia_hw_init,
- .socket_state = assabet_pcmcia_socket_state,
+ .socket_state = soc_common_cf_socket_state,
.configure_socket = assabet_pcmcia_configure_socket,
.socket_suspend = assabet_pcmcia_socket_suspend,
};
diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c
index b3774e5d0396..c3f67363f6a1 100644
--- a/drivers/pcmcia/sa1100_cerf.c
+++ b/drivers/pcmcia/sa1100_cerf.c
@@ -45,13 +45,6 @@ static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
gpio_free(CERF_GPIO_CF_RESET);
}
-static void
-cerf_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
-{
- state->vs_3v = 1;
- state->vs_Xv = 0;
-}
-
static int
cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
@@ -77,7 +70,7 @@ static struct pcmcia_low_level cerf_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = cerf_pcmcia_hw_init,
.hw_shutdown = cerf_pcmcia_hw_shutdown,
- .socket_state = cerf_pcmcia_socket_state,
+ .socket_state = soc_common_cf_socket_state,
.configure_socket = cerf_pcmcia_configure_socket,
};
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index d5ca760c4eb2..153f3122283d 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -33,6 +33,7 @@
#include <linux/cpufreq.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -42,6 +43,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
@@ -79,6 +81,41 @@ EXPORT_SYMBOL(soc_pcmcia_debug);
#define to_soc_pcmcia_socket(x) \
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
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;
- for (i = 0; i < nr; i++) {
+ for (i = 0; i < nr; i++)
if (skt->stat[i].irq)
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)
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));
}
+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)
{
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++) {
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) {
__soc_pcmcia_hw_shutdown(skt, i);
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) {
@@ -166,8 +235,6 @@ static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
IRQF_TRIGGER_NONE,
skt->stat[i].name, skt);
if (ret) {
- if (gpio_is_valid(skt->stat[i].gpio))
- gpio_free(skt->stat[i].gpio);
__soc_pcmcia_hw_shutdown(skt, i);
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);
}
+/*
+ * 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)
{
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.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);
@@ -257,7 +337,30 @@ static int soc_common_pcmcia_config_skt(
int ret;
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) {
+ 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
* may or may not be claimed by the driver.
@@ -274,10 +377,6 @@ static int soc_common_pcmcia_config_skt(
skt->cs_state = *state;
}
- if (ret < 0)
- printk(KERN_ERR "soc_common_pcmcia: unable to configure "
- "socket %d\n", skt->nr);
-
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
-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;
- 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
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;
@@ -700,19 +764,21 @@ EXPORT_SYMBOL(soc_pcmcia_init_one);
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
{
- mutex_lock(&soc_pcmcia_sockets_lock);
del_timer_sync(&skt->poll_timer);
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);
/* should not be required; violates some lowlevel drivers */
soc_common_pcmcia_config_skt(skt, &dead_socket);
- list_del(&skt->node);
- mutex_unlock(&soc_pcmcia_sockets_lock);
-
iounmap(skt->virt_io);
skt->virt_io = NULL;
release_resource(&skt->res_attr);
@@ -726,6 +792,8 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
{
int ret;
+ skt->cs_state = dead_socket;
+
setup_timer(&skt->poll_timer, soc_common_pcmcia_poll_event,
(unsigned long)skt);
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;
}
- mutex_lock(&soc_pcmcia_sockets_lock);
-
- list_add(&skt->node, &soc_pcmcia_sockets);
-
/*
* We initialize default socket timing here, because
* 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);
+#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);
if (ret)
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);
if (ret)
goto out_err_8;
@@ -791,15 +864,12 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
return ret;
out_err_8:
- mutex_lock(&soc_pcmcia_sockets_lock);
del_timer_sync(&skt->poll_timer);
pcmcia_unregister_socket(&skt->socket);
out_err_7:
soc_pcmcia_hw_shutdown(skt);
out_err_6:
- list_del(&skt->node);
- mutex_unlock(&soc_pcmcia_sockets_lock);
iounmap(skt->virt_io);
out_err_5:
release_resource(&skt->res_attr);
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
index 94762a54d731..3f3625805353 100644
--- a/drivers/pcmcia/soc_common.h
+++ b/drivers/pcmcia/soc_common.h
@@ -17,7 +17,14 @@
struct device;
+struct gpio_desc;
struct pcmcia_low_level;
+struct regulator;
+
+struct soc_pcmcia_regulator {
+ struct regulator *reg;
+ bool on;
+};
/*
* This structure encapsulates per-socket state which we might need to
@@ -52,18 +59,30 @@ struct soc_pcmcia_socket {
struct {
int gpio;
+ struct gpio_desc *desc;
unsigned int irq;
const char *name;
- } stat[4];
+ } stat[6];
#define SOC_STAT_CD 0 /* Card detect */
#define SOC_STAT_BVD1 1 /* BATDEAD / IOSTSCHG */
#define SOC_STAT_BVD2 2 /* BATWARN / IOSPKR */
#define SOC_STAT_RDY 3 /* Ready / Interrupt */
+#define SOC_STAT_VS1 4 /* Voltage sense 1 */
+#define SOC_STAT_VS2 5 /* Voltage sense 2 */
+
+ struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_bus_enable;
+ struct soc_pcmcia_regulator vcc;
+ struct soc_pcmcia_regulator vpp;
unsigned int irq_state;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block cpufreq_nb;
+#endif
struct timer_list poll_timer;
struct list_head node;
+ void *driver_data;
};
struct skt_dev_info {
@@ -133,10 +152,16 @@ struct soc_pcmcia_timing {
extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *);
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);
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt);
int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt);
+int soc_pcmcia_request_gpiods(struct soc_pcmcia_socket *skt);
+
+void soc_common_cf_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state);
+int soc_pcmcia_regulator_set(struct soc_pcmcia_socket *skt,
+ struct soc_pcmcia_regulator *r, int v);
#ifdef CONFIG_PCMCIA_DEBUG