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()
This commit is contained in:
Linus Torvalds 2016-10-10 11:34:28 -07:00
commit 911f9dab30
4 changed files with 183 additions and 102 deletions

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,22 +201,33 @@ 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,
/* 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 (irq > 0) {
if (i == SOC_STAT_RDY)
skt->socket.pci_irq = irq;
else
skt->stat[i].irq = irq;
}
}
if (skt->stat[i].irq) {
ret = request_irq(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;
return skt->ops->frequency_change(skt, val, freqs);
}
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;
}
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);

View File

@ -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