162306a36Sopenharmony_ci/*====================================================================== 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Common support code for the PCMCIA control functionality of 462306a36Sopenharmony_ci integrated SOCs like the SA-11x0 and PXA2xx microprocessors. 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci The contents of this file are subject to the Mozilla Public 762306a36Sopenharmony_ci License Version 1.1 (the "License"); you may not use this file 862306a36Sopenharmony_ci except in compliance with the License. You may obtain a copy of 962306a36Sopenharmony_ci the License at http://www.mozilla.org/MPL/ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci Software distributed under the License is distributed on an "AS 1262306a36Sopenharmony_ci IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 1362306a36Sopenharmony_ci implied. See the License for the specific language governing 1462306a36Sopenharmony_ci rights and limitations under the License. 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci The initial developer of the original code is John G. Dorsey 1762306a36Sopenharmony_ci <john+@cs.cmu.edu>. Portions created by John G. Dorsey are 1862306a36Sopenharmony_ci Copyright (C) 1999 John G. Dorsey. All Rights Reserved. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci Alternatively, the contents of this file may be used under the 2162306a36Sopenharmony_ci terms of the GNU Public License version 2 (the "GPL"), in which 2262306a36Sopenharmony_ci case the provisions of the GPL are applicable instead of the 2362306a36Sopenharmony_ci above. If you wish to allow the use of your version of this file 2462306a36Sopenharmony_ci only under the terms of the GPL and not to allow others to use 2562306a36Sopenharmony_ci your version of this file under the MPL, indicate your decision 2662306a36Sopenharmony_ci by deleting the provisions above and replace them with the notice 2762306a36Sopenharmony_ci and other provisions required by the GPL. If you do not delete 2862306a36Sopenharmony_ci the provisions above, a recipient may use your version of this 2962306a36Sopenharmony_ci file under either the MPL or the GPL. 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci======================================================================*/ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/cpufreq.h> 3562306a36Sopenharmony_ci#include <linux/gpio.h> 3662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/interrupt.h> 3962306a36Sopenharmony_ci#include <linux/io.h> 4062306a36Sopenharmony_ci#include <linux/irq.h> 4162306a36Sopenharmony_ci#include <linux/kernel.h> 4262306a36Sopenharmony_ci#include <linux/mm.h> 4362306a36Sopenharmony_ci#include <linux/module.h> 4462306a36Sopenharmony_ci#include <linux/moduleparam.h> 4562306a36Sopenharmony_ci#include <linux/mutex.h> 4662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 4762306a36Sopenharmony_ci#include <linux/spinlock.h> 4862306a36Sopenharmony_ci#include <linux/timer.h> 4962306a36Sopenharmony_ci#include <linux/pci.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include "soc_common.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_DEBUG 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int pc_debug; 5862306a36Sopenharmony_cimodule_param(pc_debug, int, 0644); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func, 6162306a36Sopenharmony_ci int lvl, const char *fmt, ...) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct va_format vaf; 6462306a36Sopenharmony_ci va_list args; 6562306a36Sopenharmony_ci if (pc_debug > lvl) { 6662306a36Sopenharmony_ci va_start(args, fmt); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci vaf.fmt = fmt; 6962306a36Sopenharmony_ci vaf.va = &args; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci printk(KERN_DEBUG "skt%u: %s: %pV", skt->nr, func, &vaf); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci va_end(args); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL(soc_pcmcia_debug); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#endif 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define to_soc_pcmcia_socket(x) \ 8162306a36Sopenharmony_ci container_of(x, struct soc_pcmcia_socket, socket) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciint soc_pcmcia_regulator_set(struct soc_pcmcia_socket *skt, 8462306a36Sopenharmony_ci struct soc_pcmcia_regulator *r, int v) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci bool on; 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!r->reg) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci on = v != 0; 9362306a36Sopenharmony_ci if (r->on == on) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (on) { 9762306a36Sopenharmony_ci ret = regulator_set_voltage(r->reg, v * 100000, v * 100000); 9862306a36Sopenharmony_ci if (ret) { 9962306a36Sopenharmony_ci int vout = regulator_get_voltage(r->reg) / 100000; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci dev_warn(&skt->socket.dev, 10262306a36Sopenharmony_ci "CS requested %s=%u.%uV, applying %u.%uV\n", 10362306a36Sopenharmony_ci r == &skt->vcc ? "Vcc" : "Vpp", 10462306a36Sopenharmony_ci v / 10, v % 10, vout / 10, vout % 10); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = regulator_enable(r->reg); 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci ret = regulator_disable(r->reg); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci if (ret == 0) 11262306a36Sopenharmony_ci r->on = on; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soc_pcmcia_regulator_set); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic unsigned short 11962306a36Sopenharmony_cicalc_speed(unsigned short *spds, int num, unsigned short dflt) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned short speed = 0; 12262306a36Sopenharmony_ci int i; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci for (i = 0; i < num; i++) 12562306a36Sopenharmony_ci if (speed < spds[i]) 12662306a36Sopenharmony_ci speed = spds[i]; 12762306a36Sopenharmony_ci if (speed == 0) 12862306a36Sopenharmony_ci speed = dflt; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return speed; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_civoid soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, 13462306a36Sopenharmony_ci struct soc_pcmcia_timing *timing) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci timing->io = 13762306a36Sopenharmony_ci calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS); 13862306a36Sopenharmony_ci timing->mem = 13962306a36Sopenharmony_ci calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); 14062306a36Sopenharmony_ci timing->attr = 14162306a36Sopenharmony_ci calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ciEXPORT_SYMBOL(soc_common_pcmcia_get_timing); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt, 14662306a36Sopenharmony_ci unsigned int nr) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < nr; i++) 15162306a36Sopenharmony_ci if (skt->stat[i].irq) 15262306a36Sopenharmony_ci free_irq(skt->stat[i].irq, skt); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (skt->ops->hw_shutdown) 15562306a36Sopenharmony_ci skt->ops->hw_shutdown(skt); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci clk_disable_unprepare(skt->clk); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci __soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat)); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciint soc_pcmcia_request_gpiods(struct soc_pcmcia_socket *skt) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct device *dev = skt->socket.dev.parent; 16862306a36Sopenharmony_ci struct gpio_desc *desc; 16962306a36Sopenharmony_ci int i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(skt->stat); i++) { 17262306a36Sopenharmony_ci if (!skt->stat[i].name) 17362306a36Sopenharmony_ci continue; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci desc = devm_gpiod_get(dev, skt->stat[i].name, GPIOD_IN); 17662306a36Sopenharmony_ci if (IS_ERR(desc)) { 17762306a36Sopenharmony_ci dev_err(dev, "Failed to get GPIO for %s: %ld\n", 17862306a36Sopenharmony_ci skt->stat[i].name, PTR_ERR(desc)); 17962306a36Sopenharmony_ci return PTR_ERR(desc); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci skt->stat[i].desc = desc; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soc_pcmcia_request_gpiods); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int ret = 0, i; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = clk_prepare_enable(skt->clk); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (skt->ops->hw_init) { 19862306a36Sopenharmony_ci ret = skt->ops->hw_init(skt); 19962306a36Sopenharmony_ci if (ret) { 20062306a36Sopenharmony_ci clk_disable_unprepare(skt->clk); 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(skt->stat); i++) { 20662306a36Sopenharmony_ci if (gpio_is_valid(skt->stat[i].gpio)) { 20762306a36Sopenharmony_ci unsigned long flags = GPIOF_IN; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* CD is active low by default */ 21062306a36Sopenharmony_ci if (i == SOC_STAT_CD) 21162306a36Sopenharmony_ci flags |= GPIOF_ACTIVE_LOW; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = devm_gpio_request_one(skt->socket.dev.parent, 21462306a36Sopenharmony_ci skt->stat[i].gpio, flags, 21562306a36Sopenharmony_ci skt->stat[i].name); 21662306a36Sopenharmony_ci if (ret) { 21762306a36Sopenharmony_ci __soc_pcmcia_hw_shutdown(skt, i); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci skt->stat[i].desc = gpio_to_desc(skt->stat[i].gpio); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (i < SOC_STAT_VS1 && skt->stat[i].desc) { 22562306a36Sopenharmony_ci int irq = gpiod_to_irq(skt->stat[i].desc); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (irq > 0) { 22862306a36Sopenharmony_ci if (i == SOC_STAT_RDY) 22962306a36Sopenharmony_ci skt->socket.pci_irq = irq; 23062306a36Sopenharmony_ci else 23162306a36Sopenharmony_ci skt->stat[i].irq = irq; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (skt->stat[i].irq) { 23662306a36Sopenharmony_ci ret = request_irq(skt->stat[i].irq, 23762306a36Sopenharmony_ci soc_common_pcmcia_interrupt, 23862306a36Sopenharmony_ci IRQF_TRIGGER_NONE, 23962306a36Sopenharmony_ci skt->stat[i].name, skt); 24062306a36Sopenharmony_ci if (ret) { 24162306a36Sopenharmony_ci __soc_pcmcia_hw_shutdown(skt, i); 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int i; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(skt->stat); i++) 25562306a36Sopenharmony_ci if (skt->stat[i].irq) { 25662306a36Sopenharmony_ci irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING); 25762306a36Sopenharmony_ci irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int i; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(skt->stat); i++) 26662306a36Sopenharmony_ci if (skt->stat[i].irq) 26762306a36Sopenharmony_ci irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * The CF 3.0 specification says that cards tie VS1 to ground and leave 27262306a36Sopenharmony_ci * VS2 open. Many implementations do not wire up the VS signals, so we 27362306a36Sopenharmony_ci * provide hard-coded values as per the CF 3.0 spec. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_civoid soc_common_cf_socket_state(struct soc_pcmcia_socket *skt, 27662306a36Sopenharmony_ci struct pcmcia_state *state) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci state->vs_3v = 1; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soc_common_cf_socket_state); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct pcmcia_state state; 28562306a36Sopenharmony_ci unsigned int stat; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci memset(&state, 0, sizeof(struct pcmcia_state)); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Make battery voltage state report 'good' */ 29062306a36Sopenharmony_ci state.bvd1 = 1; 29162306a36Sopenharmony_ci state.bvd2 = 1; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (skt->stat[SOC_STAT_CD].desc) 29462306a36Sopenharmony_ci state.detect = !!gpiod_get_value(skt->stat[SOC_STAT_CD].desc); 29562306a36Sopenharmony_ci if (skt->stat[SOC_STAT_RDY].desc) 29662306a36Sopenharmony_ci state.ready = !!gpiod_get_value(skt->stat[SOC_STAT_RDY].desc); 29762306a36Sopenharmony_ci if (skt->stat[SOC_STAT_BVD1].desc) 29862306a36Sopenharmony_ci state.bvd1 = !!gpiod_get_value(skt->stat[SOC_STAT_BVD1].desc); 29962306a36Sopenharmony_ci if (skt->stat[SOC_STAT_BVD2].desc) 30062306a36Sopenharmony_ci state.bvd2 = !!gpiod_get_value(skt->stat[SOC_STAT_BVD2].desc); 30162306a36Sopenharmony_ci if (skt->stat[SOC_STAT_VS1].desc) 30262306a36Sopenharmony_ci state.vs_3v = !!gpiod_get_value(skt->stat[SOC_STAT_VS1].desc); 30362306a36Sopenharmony_ci if (skt->stat[SOC_STAT_VS2].desc) 30462306a36Sopenharmony_ci state.vs_Xv = !!gpiod_get_value(skt->stat[SOC_STAT_VS2].desc); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci skt->ops->socket_state(skt, &state); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci stat = state.detect ? SS_DETECT : 0; 30962306a36Sopenharmony_ci stat |= state.ready ? SS_READY : 0; 31062306a36Sopenharmony_ci stat |= state.wrprot ? SS_WRPROT : 0; 31162306a36Sopenharmony_ci stat |= state.vs_3v ? SS_3VCARD : 0; 31262306a36Sopenharmony_ci stat |= state.vs_Xv ? SS_XVCARD : 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* The power status of individual sockets is not available 31562306a36Sopenharmony_ci * explicitly from the hardware, so we just remember the state 31662306a36Sopenharmony_ci * and regurgitate it upon request: 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci stat |= skt->cs_state.Vcc ? SS_POWERON : 0; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (skt->cs_state.flags & SS_IOCARD) 32162306a36Sopenharmony_ci stat |= state.bvd1 ? 0 : SS_STSCHG; 32262306a36Sopenharmony_ci else { 32362306a36Sopenharmony_ci if (state.bvd1 == 0) 32462306a36Sopenharmony_ci stat |= SS_BATDEAD; 32562306a36Sopenharmony_ci else if (state.bvd2 == 0) 32662306a36Sopenharmony_ci stat |= SS_BATWARN; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci return stat; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* 33262306a36Sopenharmony_ci * soc_common_pcmcia_config_skt 33362306a36Sopenharmony_ci * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Convert PCMCIA socket state to our socket configure structure. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_cistatic int soc_common_pcmcia_config_skt( 33862306a36Sopenharmony_ci struct soc_pcmcia_socket *skt, socket_state_t *state) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = skt->ops->configure_socket(skt, state); 34362306a36Sopenharmony_ci if (ret < 0) { 34462306a36Sopenharmony_ci pr_err("soc_common_pcmcia: unable to configure socket %d\n", 34562306a36Sopenharmony_ci skt->nr); 34662306a36Sopenharmony_ci /* restore the previous state */ 34762306a36Sopenharmony_ci WARN_ON(skt->ops->configure_socket(skt, &skt->cs_state)); 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (ret == 0) { 35262306a36Sopenharmony_ci struct gpio_desc *descs[2]; 35362306a36Sopenharmony_ci DECLARE_BITMAP(values, 2); 35462306a36Sopenharmony_ci int n = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (skt->gpio_reset) { 35762306a36Sopenharmony_ci descs[n] = skt->gpio_reset; 35862306a36Sopenharmony_ci __assign_bit(n++, values, state->flags & SS_RESET); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci if (skt->gpio_bus_enable) { 36162306a36Sopenharmony_ci descs[n] = skt->gpio_bus_enable; 36262306a36Sopenharmony_ci __assign_bit(n++, values, state->flags & SS_OUTPUT_ENA); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (n) 36662306a36Sopenharmony_ci gpiod_set_array_value_cansleep(n, descs, NULL, values); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * This really needs a better solution. The IRQ 37062306a36Sopenharmony_ci * may or may not be claimed by the driver. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (skt->irq_state != 1 && state->io_irq) { 37362306a36Sopenharmony_ci skt->irq_state = 1; 37462306a36Sopenharmony_ci irq_set_irq_type(skt->socket.pci_irq, 37562306a36Sopenharmony_ci IRQ_TYPE_EDGE_FALLING); 37662306a36Sopenharmony_ci } else if (skt->irq_state == 1 && state->io_irq == 0) { 37762306a36Sopenharmony_ci skt->irq_state = 0; 37862306a36Sopenharmony_ci irq_set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci skt->cs_state = *state; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return ret; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* soc_common_pcmcia_sock_init() 38862306a36Sopenharmony_ci * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * (Re-)Initialise the socket, turning on status interrupts 39162306a36Sopenharmony_ci * and PCMCIA bus. This must wait for power to stabilise 39262306a36Sopenharmony_ci * so that the card status signals report correctly. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Returns: 0 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_cistatic int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci debug(skt, 2, "initializing socket\n"); 40162306a36Sopenharmony_ci if (skt->ops->socket_init) 40262306a36Sopenharmony_ci skt->ops->socket_init(skt); 40362306a36Sopenharmony_ci soc_pcmcia_hw_enable(skt); 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* 40962306a36Sopenharmony_ci * soc_common_pcmcia_suspend() 41062306a36Sopenharmony_ci * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * Remove power on the socket, disable IRQs from the card. 41362306a36Sopenharmony_ci * Turn off status interrupts, and disable the PCMCIA bus. 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * Returns: 0 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_cistatic int soc_common_pcmcia_suspend(struct pcmcia_socket *sock) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci debug(skt, 2, "suspending socket\n"); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci soc_pcmcia_hw_disable(skt); 42462306a36Sopenharmony_ci if (skt->ops->socket_suspend) 42562306a36Sopenharmony_ci skt->ops->socket_suspend(skt); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(status_lock); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void soc_common_check_status(struct soc_pcmcia_socket *skt) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci unsigned int events; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci debug(skt, 4, "entering PCMCIA monitoring thread\n"); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci do { 43962306a36Sopenharmony_ci unsigned int status; 44062306a36Sopenharmony_ci unsigned long flags; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci status = soc_common_pcmcia_skt_state(skt); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci spin_lock_irqsave(&status_lock, flags); 44562306a36Sopenharmony_ci events = (status ^ skt->status) & skt->cs_state.csc_mask; 44662306a36Sopenharmony_ci skt->status = status; 44762306a36Sopenharmony_ci spin_unlock_irqrestore(&status_lock, flags); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci debug(skt, 4, "events: %s%s%s%s%s%s\n", 45062306a36Sopenharmony_ci events == 0 ? "<NONE>" : "", 45162306a36Sopenharmony_ci events & SS_DETECT ? "DETECT " : "", 45262306a36Sopenharmony_ci events & SS_READY ? "READY " : "", 45362306a36Sopenharmony_ci events & SS_BATDEAD ? "BATDEAD " : "", 45462306a36Sopenharmony_ci events & SS_BATWARN ? "BATWARN " : "", 45562306a36Sopenharmony_ci events & SS_STSCHG ? "STSCHG " : ""); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (events) 45862306a36Sopenharmony_ci pcmcia_parse_events(&skt->socket, events); 45962306a36Sopenharmony_ci } while (events); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/* Let's poll for events in addition to IRQs since IRQ only is unreliable... */ 46362306a36Sopenharmony_cistatic void soc_common_pcmcia_poll_event(struct timer_list *t) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = from_timer(skt, t, poll_timer); 46662306a36Sopenharmony_ci debug(skt, 4, "polling for events\n"); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci mod_timer(&skt->poll_timer, jiffies + SOC_PCMCIA_POLL_PERIOD); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci soc_common_check_status(skt); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* 47562306a36Sopenharmony_ci * Service routine for socket driver interrupts (requested by the 47662306a36Sopenharmony_ci * low-level PCMCIA init() operation via soc_common_pcmcia_thread()). 47762306a36Sopenharmony_ci * The actual interrupt-servicing work is performed by 47862306a36Sopenharmony_ci * soc_common_pcmcia_thread(), largely because the Card Services event- 47962306a36Sopenharmony_ci * handling code performs scheduling operations which cannot be 48062306a36Sopenharmony_ci * executed from within an interrupt context. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_cistatic irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = dev; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci debug(skt, 3, "servicing IRQ %d\n", irq); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci soc_common_check_status(skt); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return IRQ_HANDLED; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* 49562306a36Sopenharmony_ci * Implements the get_status() operation for the in-kernel PCMCIA 49662306a36Sopenharmony_ci * service (formerly SS_GetStatus in Card Services). Essentially just 49762306a36Sopenharmony_ci * fills in bits in `status' according to internal driver state or 49862306a36Sopenharmony_ci * the value of the voltage detect chipselect register. 49962306a36Sopenharmony_ci * 50062306a36Sopenharmony_ci * As a debugging note, during card startup, the PCMCIA core issues 50162306a36Sopenharmony_ci * three set_socket() commands in a row the first with RESET deasserted, 50262306a36Sopenharmony_ci * the second with RESET asserted, and the last with RESET deasserted 50362306a36Sopenharmony_ci * again. Following the third set_socket(), a get_status() command will 50462306a36Sopenharmony_ci * be issued. The kernel is looking for the SS_READY flag (see 50562306a36Sopenharmony_ci * setup_socket(), reset_socket(), and unreset_socket() in cs.c). 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Returns: 0 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistatic int 51062306a36Sopenharmony_cisoc_common_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci skt->status = soc_common_pcmcia_skt_state(skt); 51562306a36Sopenharmony_ci *status = skt->status; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/* 52262306a36Sopenharmony_ci * Implements the set_socket() operation for the in-kernel PCMCIA 52362306a36Sopenharmony_ci * service (formerly SS_SetSocket in Card Services). We more or 52462306a36Sopenharmony_ci * less punt all of this work and let the kernel handle the details 52562306a36Sopenharmony_ci * of power configuration, reset, &c. We also record the value of 52662306a36Sopenharmony_ci * `state' in order to regurgitate it to the PCMCIA core later. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_cistatic int soc_common_pcmcia_set_socket( 52962306a36Sopenharmony_ci struct pcmcia_socket *sock, socket_state_t *state) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci debug(skt, 2, "mask: %s%s%s%s%s%s flags: %s%s%s%s%s%s Vcc %d Vpp %d irq %d\n", 53462306a36Sopenharmony_ci (state->csc_mask == 0) ? "<NONE> " : "", 53562306a36Sopenharmony_ci (state->csc_mask & SS_DETECT) ? "DETECT " : "", 53662306a36Sopenharmony_ci (state->csc_mask & SS_READY) ? "READY " : "", 53762306a36Sopenharmony_ci (state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "", 53862306a36Sopenharmony_ci (state->csc_mask & SS_BATWARN) ? "BATWARN " : "", 53962306a36Sopenharmony_ci (state->csc_mask & SS_STSCHG) ? "STSCHG " : "", 54062306a36Sopenharmony_ci (state->flags == 0) ? "<NONE> " : "", 54162306a36Sopenharmony_ci (state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "", 54262306a36Sopenharmony_ci (state->flags & SS_IOCARD) ? "IOCARD " : "", 54362306a36Sopenharmony_ci (state->flags & SS_RESET) ? "RESET " : "", 54462306a36Sopenharmony_ci (state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "", 54562306a36Sopenharmony_ci (state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "", 54662306a36Sopenharmony_ci state->Vcc, state->Vpp, state->io_irq); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return soc_common_pcmcia_config_skt(skt, state); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/* 55362306a36Sopenharmony_ci * Implements the set_io_map() operation for the in-kernel PCMCIA 55462306a36Sopenharmony_ci * service (formerly SS_SetIOMap in Card Services). We configure 55562306a36Sopenharmony_ci * the map speed as requested, but override the address ranges 55662306a36Sopenharmony_ci * supplied by Card Services. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Returns: 0 on success, -1 on error 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic int soc_common_pcmcia_set_io_map( 56162306a36Sopenharmony_ci struct pcmcia_socket *sock, struct pccard_io_map *map) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 56462306a36Sopenharmony_ci unsigned short speed = map->speed; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci debug(skt, 2, "map %u speed %u start 0x%08llx stop 0x%08llx\n", 56762306a36Sopenharmony_ci map->map, map->speed, (unsigned long long)map->start, 56862306a36Sopenharmony_ci (unsigned long long)map->stop); 56962306a36Sopenharmony_ci debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", 57062306a36Sopenharmony_ci (map->flags == 0) ? "<NONE>" : "", 57162306a36Sopenharmony_ci (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", 57262306a36Sopenharmony_ci (map->flags & MAP_16BIT) ? "16BIT " : "", 57362306a36Sopenharmony_ci (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", 57462306a36Sopenharmony_ci (map->flags & MAP_0WS) ? "0WS " : "", 57562306a36Sopenharmony_ci (map->flags & MAP_WRPROT) ? "WRPROT " : "", 57662306a36Sopenharmony_ci (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "", 57762306a36Sopenharmony_ci (map->flags & MAP_PREFETCH) ? "PREFETCH " : ""); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (map->map >= MAX_IO_WIN) { 58062306a36Sopenharmony_ci printk(KERN_ERR "%s(): map (%d) out of range\n", __func__, 58162306a36Sopenharmony_ci map->map); 58262306a36Sopenharmony_ci return -1; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (map->flags & MAP_ACTIVE) { 58662306a36Sopenharmony_ci if (speed == 0) 58762306a36Sopenharmony_ci speed = SOC_PCMCIA_IO_ACCESS; 58862306a36Sopenharmony_ci } else { 58962306a36Sopenharmony_ci speed = 0; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci skt->spd_io[map->map] = speed; 59362306a36Sopenharmony_ci skt->ops->set_timing(skt); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (map->stop == 1) 59662306a36Sopenharmony_ci map->stop = PAGE_SIZE-1; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci map->stop -= map->start; 59962306a36Sopenharmony_ci map->stop += skt->socket.io_offset; 60062306a36Sopenharmony_ci map->start = skt->socket.io_offset; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* 60762306a36Sopenharmony_ci * Implements the set_mem_map() operation for the in-kernel PCMCIA 60862306a36Sopenharmony_ci * service (formerly SS_SetMemMap in Card Services). We configure 60962306a36Sopenharmony_ci * the map speed as requested, but override the address ranges 61062306a36Sopenharmony_ci * supplied by Card Services. 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * Returns: 0 on success, -ERRNO on error 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_cistatic int soc_common_pcmcia_set_mem_map( 61562306a36Sopenharmony_ci struct pcmcia_socket *sock, struct pccard_mem_map *map) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); 61862306a36Sopenharmony_ci struct resource *res; 61962306a36Sopenharmony_ci unsigned short speed = map->speed; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci debug(skt, 2, "map %u speed %u card_start %08x\n", 62262306a36Sopenharmony_ci map->map, map->speed, map->card_start); 62362306a36Sopenharmony_ci debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", 62462306a36Sopenharmony_ci (map->flags == 0) ? "<NONE>" : "", 62562306a36Sopenharmony_ci (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", 62662306a36Sopenharmony_ci (map->flags & MAP_16BIT) ? "16BIT " : "", 62762306a36Sopenharmony_ci (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", 62862306a36Sopenharmony_ci (map->flags & MAP_0WS) ? "0WS " : "", 62962306a36Sopenharmony_ci (map->flags & MAP_WRPROT) ? "WRPROT " : "", 63062306a36Sopenharmony_ci (map->flags & MAP_ATTRIB) ? "ATTRIB " : "", 63162306a36Sopenharmony_ci (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : ""); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (map->map >= MAX_WIN) 63462306a36Sopenharmony_ci return -EINVAL; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (map->flags & MAP_ACTIVE) { 63762306a36Sopenharmony_ci if (speed == 0) 63862306a36Sopenharmony_ci speed = 300; 63962306a36Sopenharmony_ci } else { 64062306a36Sopenharmony_ci speed = 0; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (map->flags & MAP_ATTRIB) { 64462306a36Sopenharmony_ci res = &skt->res_attr; 64562306a36Sopenharmony_ci skt->spd_attr[map->map] = speed; 64662306a36Sopenharmony_ci skt->spd_mem[map->map] = 0; 64762306a36Sopenharmony_ci } else { 64862306a36Sopenharmony_ci res = &skt->res_mem; 64962306a36Sopenharmony_ci skt->spd_attr[map->map] = 0; 65062306a36Sopenharmony_ci skt->spd_mem[map->map] = speed; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci skt->ops->set_timing(skt); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci map->static_start = res->start + map->card_start; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistruct bittbl { 66162306a36Sopenharmony_ci unsigned int mask; 66262306a36Sopenharmony_ci const char *name; 66362306a36Sopenharmony_ci}; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic struct bittbl status_bits[] = { 66662306a36Sopenharmony_ci { SS_WRPROT, "SS_WRPROT" }, 66762306a36Sopenharmony_ci { SS_BATDEAD, "SS_BATDEAD" }, 66862306a36Sopenharmony_ci { SS_BATWARN, "SS_BATWARN" }, 66962306a36Sopenharmony_ci { SS_READY, "SS_READY" }, 67062306a36Sopenharmony_ci { SS_DETECT, "SS_DETECT" }, 67162306a36Sopenharmony_ci { SS_POWERON, "SS_POWERON" }, 67262306a36Sopenharmony_ci { SS_STSCHG, "SS_STSCHG" }, 67362306a36Sopenharmony_ci { SS_3VCARD, "SS_3VCARD" }, 67462306a36Sopenharmony_ci { SS_XVCARD, "SS_XVCARD" }, 67562306a36Sopenharmony_ci}; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic struct bittbl conf_bits[] = { 67862306a36Sopenharmony_ci { SS_PWR_AUTO, "SS_PWR_AUTO" }, 67962306a36Sopenharmony_ci { SS_IOCARD, "SS_IOCARD" }, 68062306a36Sopenharmony_ci { SS_RESET, "SS_RESET" }, 68162306a36Sopenharmony_ci { SS_DMA_MODE, "SS_DMA_MODE" }, 68262306a36Sopenharmony_ci { SS_SPKR_ENA, "SS_SPKR_ENA" }, 68362306a36Sopenharmony_ci { SS_OUTPUT_ENA, "SS_OUTPUT_ENA" }, 68462306a36Sopenharmony_ci}; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic void dump_bits(char **p, const char *prefix, 68762306a36Sopenharmony_ci unsigned int val, struct bittbl *bits, int sz) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci char *b = *p; 69062306a36Sopenharmony_ci int i; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci b += sprintf(b, "%-9s:", prefix); 69362306a36Sopenharmony_ci for (i = 0; i < sz; i++) 69462306a36Sopenharmony_ci if (val & bits[i].mask) 69562306a36Sopenharmony_ci b += sprintf(b, " %s", bits[i].name); 69662306a36Sopenharmony_ci *b++ = '\n'; 69762306a36Sopenharmony_ci *p = b; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * Implements the /sys/class/pcmcia_socket/??/status file. 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * Returns: the number of characters added to the buffer 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic ssize_t show_status( 70662306a36Sopenharmony_ci struct device *dev, struct device_attribute *attr, char *buf) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = 70962306a36Sopenharmony_ci container_of(dev, struct soc_pcmcia_socket, socket.dev); 71062306a36Sopenharmony_ci char *p = buf; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci p += sprintf(p, "slot : %d\n", skt->nr); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dump_bits(&p, "status", skt->status, 71562306a36Sopenharmony_ci status_bits, ARRAY_SIZE(status_bits)); 71662306a36Sopenharmony_ci dump_bits(&p, "csc_mask", skt->cs_state.csc_mask, 71762306a36Sopenharmony_ci status_bits, ARRAY_SIZE(status_bits)); 71862306a36Sopenharmony_ci dump_bits(&p, "cs_flags", skt->cs_state.flags, 71962306a36Sopenharmony_ci conf_bits, ARRAY_SIZE(conf_bits)); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci p += sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); 72262306a36Sopenharmony_ci p += sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); 72362306a36Sopenharmony_ci p += sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, 72462306a36Sopenharmony_ci skt->socket.pci_irq); 72562306a36Sopenharmony_ci if (skt->ops->show_timing) 72662306a36Sopenharmony_ci p += skt->ops->show_timing(skt, p); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return p-buf; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_cistatic DEVICE_ATTR(status, S_IRUGO, show_status, NULL); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic struct pccard_operations soc_common_pcmcia_operations = { 73462306a36Sopenharmony_ci .init = soc_common_pcmcia_sock_init, 73562306a36Sopenharmony_ci .suspend = soc_common_pcmcia_suspend, 73662306a36Sopenharmony_ci .get_status = soc_common_pcmcia_get_status, 73762306a36Sopenharmony_ci .set_socket = soc_common_pcmcia_set_socket, 73862306a36Sopenharmony_ci .set_io_map = soc_common_pcmcia_set_io_map, 73962306a36Sopenharmony_ci .set_mem_map = soc_common_pcmcia_set_mem_map, 74062306a36Sopenharmony_ci}; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 74462306a36Sopenharmony_cistatic int soc_common_pcmcia_cpufreq_nb(struct notifier_block *nb, 74562306a36Sopenharmony_ci unsigned long val, void *data) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct soc_pcmcia_socket *skt = container_of(nb, struct soc_pcmcia_socket, cpufreq_nb); 74862306a36Sopenharmony_ci struct cpufreq_freqs *freqs = data; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return skt->ops->frequency_change(skt, val, freqs); 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci#endif 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_civoid soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, 75562306a36Sopenharmony_ci const struct pcmcia_low_level *ops, struct device *dev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int i; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci skt->ops = ops; 76062306a36Sopenharmony_ci skt->socket.owner = ops->owner; 76162306a36Sopenharmony_ci skt->socket.dev.parent = dev; 76262306a36Sopenharmony_ci skt->socket.pci_irq = NO_IRQ; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(skt->stat); i++) 76562306a36Sopenharmony_ci skt->stat[i].gpio = -EINVAL; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL(soc_pcmcia_init_one); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_civoid soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci del_timer_sync(&skt->poll_timer); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci pcmcia_unregister_socket(&skt->socket); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 77662306a36Sopenharmony_ci if (skt->ops->frequency_change) 77762306a36Sopenharmony_ci cpufreq_unregister_notifier(&skt->cpufreq_nb, 77862306a36Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 77962306a36Sopenharmony_ci#endif 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci soc_pcmcia_hw_shutdown(skt); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* should not be required; violates some lowlevel drivers */ 78462306a36Sopenharmony_ci soc_common_pcmcia_config_skt(skt, &dead_socket); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci iounmap(PCI_IOBASE + skt->res_io_io.start); 78762306a36Sopenharmony_ci release_resource(&skt->res_attr); 78862306a36Sopenharmony_ci release_resource(&skt->res_mem); 78962306a36Sopenharmony_ci release_resource(&skt->res_io); 79062306a36Sopenharmony_ci release_resource(&skt->res_skt); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ciEXPORT_SYMBOL(soc_pcmcia_remove_one); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ciint soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci int ret; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci skt->cs_state = dead_socket; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci timer_setup(&skt->poll_timer, soc_common_pcmcia_poll_event, 0); 80162306a36Sopenharmony_ci skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = request_resource(&iomem_resource, &skt->res_skt); 80462306a36Sopenharmony_ci if (ret) 80562306a36Sopenharmony_ci goto out_err_1; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci ret = request_resource(&skt->res_skt, &skt->res_io); 80862306a36Sopenharmony_ci if (ret) 80962306a36Sopenharmony_ci goto out_err_2; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ret = request_resource(&skt->res_skt, &skt->res_mem); 81262306a36Sopenharmony_ci if (ret) 81362306a36Sopenharmony_ci goto out_err_3; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci ret = request_resource(&skt->res_skt, &skt->res_attr); 81662306a36Sopenharmony_ci if (ret) 81762306a36Sopenharmony_ci goto out_err_4; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci skt->res_io_io = (struct resource) 82062306a36Sopenharmony_ci DEFINE_RES_IO_NAMED(skt->nr * 0x1000 + 0x10000, 0x1000, 82162306a36Sopenharmony_ci "PCMCIA I/O"); 82262306a36Sopenharmony_ci ret = pci_remap_iospace(&skt->res_io_io, skt->res_io.start); 82362306a36Sopenharmony_ci if (ret) 82462306a36Sopenharmony_ci goto out_err_5; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* 82762306a36Sopenharmony_ci * We initialize default socket timing here, because 82862306a36Sopenharmony_ci * we are not guaranteed to see a SetIOMap operation at 82962306a36Sopenharmony_ci * runtime. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci skt->ops->set_timing(skt); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ret = soc_pcmcia_hw_init(skt); 83462306a36Sopenharmony_ci if (ret) 83562306a36Sopenharmony_ci goto out_err_6; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci skt->socket.ops = &soc_common_pcmcia_operations; 83862306a36Sopenharmony_ci skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; 83962306a36Sopenharmony_ci skt->socket.resource_ops = &pccard_static_ops; 84062306a36Sopenharmony_ci skt->socket.irq_mask = 0; 84162306a36Sopenharmony_ci skt->socket.map_size = PAGE_SIZE; 84262306a36Sopenharmony_ci skt->socket.io_offset = (unsigned long)skt->res_io_io.start; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci skt->status = soc_common_pcmcia_skt_state(skt); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 84762306a36Sopenharmony_ci if (skt->ops->frequency_change) { 84862306a36Sopenharmony_ci skt->cpufreq_nb.notifier_call = soc_common_pcmcia_cpufreq_nb; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ret = cpufreq_register_notifier(&skt->cpufreq_nb, 85162306a36Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 85262306a36Sopenharmony_ci if (ret < 0) 85362306a36Sopenharmony_ci dev_err(skt->socket.dev.parent, 85462306a36Sopenharmony_ci "unable to register CPU frequency change notifier for PCMCIA (%d)\n", 85562306a36Sopenharmony_ci ret); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci#endif 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ret = pcmcia_register_socket(&skt->socket); 86062306a36Sopenharmony_ci if (ret) 86162306a36Sopenharmony_ci goto out_err_7; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci ret = device_create_file(&skt->socket.dev, &dev_attr_status); 86462306a36Sopenharmony_ci if (ret) 86562306a36Sopenharmony_ci goto out_err_8; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci out_err_8: 87062306a36Sopenharmony_ci del_timer_sync(&skt->poll_timer); 87162306a36Sopenharmony_ci pcmcia_unregister_socket(&skt->socket); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci out_err_7: 87462306a36Sopenharmony_ci soc_pcmcia_hw_shutdown(skt); 87562306a36Sopenharmony_ci out_err_6: 87662306a36Sopenharmony_ci iounmap(PCI_IOBASE + skt->res_io_io.start); 87762306a36Sopenharmony_ci out_err_5: 87862306a36Sopenharmony_ci release_resource(&skt->res_attr); 87962306a36Sopenharmony_ci out_err_4: 88062306a36Sopenharmony_ci release_resource(&skt->res_mem); 88162306a36Sopenharmony_ci out_err_3: 88262306a36Sopenharmony_ci release_resource(&skt->res_io); 88362306a36Sopenharmony_ci out_err_2: 88462306a36Sopenharmony_ci release_resource(&skt->res_skt); 88562306a36Sopenharmony_ci out_err_1: 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return ret; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ciEXPORT_SYMBOL(soc_pcmcia_add_one); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ciMODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>"); 89262306a36Sopenharmony_ciMODULE_DESCRIPTION("Linux PCMCIA Card Services: Common SoC support"); 89362306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL"); 894