From b8ed2eca34fc3d57d2eadbc31c12a521fe08e221 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 15 Sep 2016 01:38:27 +0100 Subject: ARM: sa1100/h3xxx: sleeve support Signed-off-by: Russell King --- arch/arm/mach-sa1100/Makefile | 2 + arch/arm/mach-sa1100/h3600.c | 13 ++ arch/arm/mach-sa1100/h3xxx-sleeve.c | 341 ++++++++++++++++++++++++++++++++ arch/arm/mach-sa1100/h3xxx.c | 6 + arch/arm/mach-sa1100/ipaq-sleeve-bus.c | 124 ++++++++++++ arch/arm/mach-sa1100/ipaq-sleeve-cf.c | 70 +++++++ arch/arm/mach-sa1100/ipaq-sleeve-dual.c | 68 +++++++ drivers/pcmcia/sa1100_h3600.c | 60 +----- include/linux/ipaq-sleeve.h | 34 ++++ 9 files changed, 659 insertions(+), 59 deletions(-) create mode 100644 arch/arm/mach-sa1100/h3xxx-sleeve.c create mode 100644 arch/arm/mach-sa1100/ipaq-sleeve-bus.c create mode 100644 arch/arm/mach-sa1100/ipaq-sleeve-cf.c create mode 100644 arch/arm/mach-sa1100/ipaq-sleeve-dual.c create mode 100644 include/linux/ipaq-sleeve.h diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index 28c1cae0053f..80eb60109589 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_SA1100_COLLIE) += collie.o obj-$(CONFIG_SA1100_H3100) += h3100.o h3xxx.o obj-$(CONFIG_SA1100_H3600) += h3600.o h3xxx.o +obj-m += h3xxx-sleeve.o ipaq-sleeve-bus.o \ + ipaq-sleeve-cf.o ipaq-sleeve-dual.o obj-$(CONFIG_SA1100_HACKKIT) += hackkit.o diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c index 5f4fb3027799..28ce2d1353c7 100644 --- a/arch/arm/mach-sa1100/h3600.c +++ b/arch/arm/mach-sa1100/h3600.c @@ -104,9 +104,22 @@ static struct gpiod_lookup_table h3600_irda_gpio_table = { }, }; +static struct gpiod_lookup_table h3600_sleeve_gpio_table = { + .dev_id = "ipaq-h3xxx-sleeve", + .table = { + GPIO_LOOKUP("gpio", H3600_GPIO_OPT_DET, + "option-detect", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("htc-egpio", 2, "option-reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("htc-egpio", 4, "nvram-on", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("htc-egpio", 5, "option-on", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static void __init h3600_mach_init(void) { gpiod_add_lookup_table(&h3600_irda_gpio_table); + gpiod_add_lookup_table(&h3600_sleeve_gpio_table); h3xxx_mach_init(); diff --git a/arch/arm/mach-sa1100/h3xxx-sleeve.c b/arch/arm/mach-sa1100/h3xxx-sleeve.c new file mode 100644 index 000000000000..ed0edaf43d9c --- /dev/null +++ b/arch/arm/mach-sa1100/h3xxx-sleeve.c @@ -0,0 +1,341 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLEEVE_POWER_DELAY_MS 10 +#define SLEEVE_NVRAM_POWER_MS 5 + +struct opt_info { + struct device *dev; + struct ipaq_option_id id; + struct delayed_work detect_work; + bool detect_disabled; + bool sleeve_changed; + bool sleeve_present; + bool sleeve_wakeup; + struct gpio_desc *detect; + struct gpio_desc *nvram_on; + struct gpio_desc *opt_on; + struct gpio_desc *opt_reset; +}; + +static int ipaq_option_spi_read(void *buf, unsigned int addr, size_t len) +{ + struct device *mdev = bus_find_device_by_name(&platform_bus_type, NULL, + "ipaq-h3xxx-micro"); + struct ipaq_micro *micro; + struct ipaq_micro_msg msg; + + memset(buf, 0, len); + + if (!mdev || !mdev->driver) + return -EINVAL; + + micro = dev_get_drvdata(mdev); + if (!micro) + return -EINVAL; + + memset(&msg, 0, sizeof(msg)); + + while (len) { + size_t this_len = len; + + if (this_len > 8) + this_len = 8; + + msg.id = MSG_SPI_READ; + msg.tx_len = 3; + msg.tx_data[0] = addr; + msg.tx_data[1] = addr >> 8; + msg.tx_data[2] = this_len; + + ipaq_micro_tx_msg_sync(micro, &msg); + + memcpy(buf, msg.rx_data, msg.rx_len); + + buf += msg.rx_len; + len -= msg.rx_len; + addr += msg.rx_len; + } + + return 0; +} + +static int ipaq_option_read_id(struct opt_info *opt, struct ipaq_option_id *id) +{ + __le16 vendor, device; + int ret; + + /* Enable the CF bus */ + gpiod_set_value_cansleep(opt->nvram_on, 1); + + /* Wait for power to stabilise */ + msleep(SLEEVE_NVRAM_POWER_MS); + + ret = ipaq_option_spi_read(&vendor, 6, 2); + if (ret) + goto err; + + ret = ipaq_option_spi_read(&device, 8, 2); + if (ret) + goto err; + + id->vendor = le16_to_cpu(vendor); + id->device = le16_to_cpu(device); + + err: + gpiod_set_value_cansleep(opt->nvram_on, 0); + return ret; +} + +static void ipaq_option_power_on(struct opt_info *opt, bool noirq) +{ + gpiod_set_value(opt->opt_reset, 1); + gpiod_set_value(opt->opt_on, 1); + gpiod_set_value(opt->opt_reset, 0); + + if (noirq) + mdelay(SLEEVE_POWER_DELAY_MS); + else + msleep(SLEEVE_POWER_DELAY_MS); +} + +static void ipaq_option_power_off(struct opt_info *opt, bool noirq) +{ + gpiod_set_value(opt->opt_reset, 1); + gpiod_set_value(opt->opt_on, 0); +} + +static void ipaq_option_insert(struct opt_info *opt) +{ + int ret; + + ret = ipaq_option_read_id(opt, &opt->id); + if (ret) + return; + + dev_info(opt->dev, "Option pack %04x:%04x detected\n", + opt->id.vendor, opt->id.device); + + ipaq_option_power_on(opt, false); + + if (!opt->sleeve_present) + ipaq_option_device_add(opt->dev, opt->id); + opt->sleeve_present = true; +} + +static void ipaq_option_remove(struct opt_info *opt) +{ + if (opt->sleeve_present) + ipaq_option_device_del(); + opt->sleeve_present = false; + + ipaq_option_power_off(opt, false); +} + +static void ipaq_option_work(struct work_struct *work) +{ + struct opt_info *opt = container_of(work, struct opt_info, detect_work.work); + int present = gpiod_get_value_cansleep(opt->detect); + + if (opt->sleeve_changed) + ipaq_option_remove(opt); + + if (present) + ipaq_option_insert(opt); + else + ipaq_option_remove(opt); +} + +static irqreturn_t ipaq_option_irq(int irq, void *data) +{ + struct opt_info *opt = data; + + dev_info(opt->dev, "irq: option pack: %u\n", gpiod_get_value(opt->detect)); + + if (!opt->detect_disabled) + mod_delayed_work(system_wq, &opt->detect_work, + msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +static int ipaq_h3xxx_sleeve_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct opt_info *opt; + int irq, ret; + + opt = devm_kzalloc(dev, sizeof(*opt), GFP_KERNEL); + if (!opt) + return -ENOMEM; + + platform_set_drvdata(pdev, opt); + opt->dev = dev; + INIT_DELAYED_WORK(&opt->detect_work, ipaq_option_work); + + opt->nvram_on = devm_gpiod_get(dev, "nvram-on", GPIOD_OUT_LOW); + if (IS_ERR(opt->nvram_on)) + return PTR_ERR(opt->nvram_on); + + opt->opt_on = devm_gpiod_get(dev, "option-on", GPIOD_OUT_LOW); + if (IS_ERR(opt->opt_on)) + return PTR_ERR(opt->opt_on); + + opt->opt_reset = devm_gpiod_get(dev, "option-reset", GPIOD_OUT_LOW); + if (IS_ERR(opt->opt_reset)) + return PTR_ERR(opt->opt_reset); + + opt->detect = devm_gpiod_get_optional(dev, "option-detect", GPIOD_IN); + if (IS_ERR(opt->detect)) + return PTR_ERR(opt->detect); + + if (opt->detect) { + irq = gpiod_to_irq(opt->detect); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, ipaq_option_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(dev), opt); + if (ret) + return ret; + } + + schedule_delayed_work(&opt->detect_work, 0); + flush_delayed_work(&opt->detect_work); + + return 0; +} + +static int ipaq_h3xxx_sleeve_remove(struct platform_device *pdev) +{ + struct opt_info *opt = platform_get_drvdata(pdev); + + opt->detect_disabled = true; + cancel_delayed_work_sync(&opt->detect_work); + + ipaq_option_remove(opt); + + return 0; +} + +static int ipaq_h3xxx_check_wakeup(struct device *dev, void *data) +{ + if (device_may_wakeup(dev)) + return 1; + + return device_for_each_child(dev, data, ipaq_h3xxx_check_wakeup); +} + +static int ipaq_h3xxx_sleeve_suspend(struct device *dev) +{ + struct opt_info *opt = dev_get_drvdata(dev); + int may_wakeup; + + opt->detect_disabled = true; + cancel_delayed_work_sync(&opt->detect_work); + + /* Disable sleeve power if device isn't a wakeup source */ + may_wakeup = device_for_each_child(dev, NULL, ipaq_h3xxx_check_wakeup); + + opt->sleeve_wakeup = may_wakeup; + + return 0; +} + +static int ipaq_h3xxx_sleeve_suspend_noirq(struct device *dev) +{ + struct opt_info *opt = dev_get_drvdata(dev); + + if (!opt->sleeve_wakeup && opt->sleeve_present) { + ipaq_option_power_off(opt, true); + + /* + * Give the power time to collapse before bringing the + * reset signal to logic 0. + */ + mdelay(1); + gpiod_set_raw_value(opt->opt_reset, 0); + } + + return 0; +} + +static int ipaq_h3xxx_sleeve_resume_noirq(struct device *dev) +{ + struct opt_info *opt = dev_get_drvdata(dev); + + if (!opt->sleeve_wakeup && gpiod_get_value(opt->detect)) { + /* + * We can't check the ID of the sleeve here, so assume that + * it is the same sleeve inserted, and power it up. This + * avoids an eject/insert cycle on any inserted PCMCIA card. + */ + ipaq_option_power_on(opt, true); + } + + return 0; +} + +static int ipaq_h3xxx_sleeve_resume(struct device *dev) +{ + struct opt_info *opt = dev_get_drvdata(dev); + struct ipaq_option_id id; + int ret; + + if (opt->sleeve_present && gpiod_get_value_cansleep(opt->detect)) { + ret = ipaq_option_read_id(opt, &id); + if (ret) { + dev_warn(opt->dev, "unable to read sleeve id: %d\n", + ret); + opt->sleeve_changed = true; + goto finish; + } + + if (opt->id.vendor != id.vendor || + opt->id.device != id.device) { + /* sleeve changed */ + dev_warn(opt->dev, "sleeve changed\n"); + opt->sleeve_changed = true; + } else { + dev_info(opt->dev, "same sleeve type inserted\n"); + opt->detect_disabled = false; + return 0; + } + } + +finish: + opt->detect_disabled = false; + schedule_delayed_work(&opt->detect_work, 0); + flush_delayed_work(&opt->detect_work); + + return 0; +} + +static struct dev_pm_ops ipaq_h3xxx_sleeve_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(ipaq_h3xxx_sleeve_suspend_noirq, + ipaq_h3xxx_sleeve_resume_noirq) + SET_SYSTEM_SLEEP_PM_OPS(ipaq_h3xxx_sleeve_suspend, + ipaq_h3xxx_sleeve_resume) +}; + +static struct platform_driver ipaq_h3xxx_sleeve = { + .driver = { + .name = "ipaq-h3xxx-sleeve", + .pm = &ipaq_h3xxx_sleeve_pm, + }, + .probe = ipaq_h3xxx_sleeve_probe, + .remove = ipaq_h3xxx_sleeve_remove, +}; + +module_platform_driver(ipaq_h3xxx_sleeve); + +MODULE_ALIAS("platform:ipaq-h3xxx-sleeve"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c index baf62ef2ad70..9a44a31626b9 100644 --- a/arch/arm/mach-sa1100/h3xxx.c +++ b/arch/arm/mach-sa1100/h3xxx.c @@ -183,10 +183,16 @@ struct platform_device h3xxx_micro_asic = { .num_resources = ARRAY_SIZE(h3xxx_micro_resources), }; +static struct platform_device h3xxx_sleeve = { + .name = "ipaq-h3xxx-sleeve", + .id = -1, +}; + static struct platform_device *h3xxx_devices[] = { &h3xxx_egpio, &h3xxx_keys, &h3xxx_micro_asic, + &h3xxx_sleeve, }; static struct gpiod_lookup_table h3xxx_pcmcia_gpio_table = { diff --git a/arch/arm/mach-sa1100/ipaq-sleeve-bus.c b/arch/arm/mach-sa1100/ipaq-sleeve-bus.c new file mode 100644 index 000000000000..9bf3201002b6 --- /dev/null +++ b/arch/arm/mach-sa1100/ipaq-sleeve-bus.c @@ -0,0 +1,124 @@ +#include +#include +#include + +#define to_ipaq_option_driver(x) \ + container_of(x, struct ipaq_option_driver, driver) + +static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + return snprintf(buf, PAGE_SIZE, "%04x\n", idev->id.vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t device_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + return snprintf(buf, PAGE_SIZE, "%04x\n", idev->id.device); +} +static DEVICE_ATTR_RO(device); + +static struct attribute *ipaq_option_sleeve_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_device.attr, + NULL, +}; + +static const struct attribute_group ipaq_option_sleeve_dev_group = { + .attrs = ipaq_option_sleeve_attrs, +}; + +static const struct attribute_group *ipaq_option_sleeve_dev_groups[] = { + &ipaq_option_sleeve_dev_group, + NULL, +}; + +static int ipaq_option_sleeve_match(struct device *dev, struct device_driver *drv) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + struct ipaq_option_driver *idrv = to_ipaq_option_driver(drv); + const struct ipaq_option_id *id; + + for (id = idrv->id_table; id->vendor || id->device; id++) + if (id->vendor == idev->id.vendor && + id->device == idev->id.device) + return 1; + + return 0; +} + +static int ipaq_option_sleeve_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + + if (!dev) + return -ENODEV; + + if (add_uevent_var(env, "MODALIAS=ipaq:v%04Xd%04X", + idev->id.vendor, idev->id.device)) + return -ENOMEM; + + return 0; +} + +struct bus_type ipaq_option_sleeve_bus = { + .name = "ipaq-sleeve", + .dev_groups = ipaq_option_sleeve_dev_groups, + .match = ipaq_option_sleeve_match, + .uevent = ipaq_option_sleeve_uevent, +}; +EXPORT_SYMBOL_GPL(ipaq_option_sleeve_bus); + +static void ipaq_option_release(struct device *dev) +{ + struct ipaq_option_device *opt_dev = to_ipaq_option_device(dev); + + kfree(opt_dev); +} + +int ipaq_option_device_add(struct device *parent, struct ipaq_option_id id) +{ + struct ipaq_option_device *idev; + + idev = kzalloc(sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + idev->id = id; + device_initialize(&idev->dev); + dev_set_name(&idev->dev, "sleeve"); + idev->dev.parent = parent; + idev->dev.release = ipaq_option_release; + idev->dev.bus = &ipaq_option_sleeve_bus; + + return device_add(&idev->dev); +} +EXPORT_SYMBOL_GPL(ipaq_option_device_add); + +static int ipaq_option_device_remove(struct device *dev, void *data) +{ + device_unregister(dev); + return 0; +} + +void ipaq_option_device_del(void) +{ + bus_for_each_dev(&ipaq_option_sleeve_bus, NULL, NULL, + ipaq_option_device_remove); +} +EXPORT_SYMBOL_GPL(ipaq_option_device_del); + +static int ipaq_option_sleeve_init(void) +{ + return bus_register(&ipaq_option_sleeve_bus); +} +core_initcall(ipaq_option_sleeve_init); + +static void ipaq_option_sleeve_exit(void) +{ + bus_unregister(&ipaq_option_sleeve_bus); +} +module_exit(ipaq_option_sleeve_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-sa1100/ipaq-sleeve-cf.c b/arch/arm/mach-sa1100/ipaq-sleeve-cf.c new file mode 100644 index 000000000000..8f0b8ae8b75c --- /dev/null +++ b/arch/arm/mach-sa1100/ipaq-sleeve-cf.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +struct ipaq_sleeve_cf_data { + struct platform_device *skt; +}; + +static int ipaq_sleeve_cf_probe(struct device *dev) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + struct ipaq_sleeve_cf_data *cf; + struct platform_device_info pinfo = { + .parent = dev, + .name = "sa11x0-pcmcia", + .id = 0, + .data = &idev->id, + .size_data = sizeof(idev->id), + }; + + cf = devm_kzalloc(dev, sizeof(*cf), GFP_KERNEL); + if (!cf) + return -ENOMEM; + + cf->skt = platform_device_register_full(&pinfo); + if (IS_ERR(cf->skt)) { + dev_err(dev, "unable to register pcmcia device: %ld\n", + PTR_ERR(cf->skt)); + return PTR_ERR(cf->skt); + } + + dev_set_drvdata(dev, cf); + + return 0; +} + +static int ipaq_sleeve_cf_remove(struct device *dev) +{ + struct ipaq_sleeve_cf_data *cf = dev_get_drvdata(dev); + + platform_device_unregister(cf->skt); + + return 0; +} + +static const struct ipaq_option_id ipaq_sleeve_cf_ids[] = { + { /* Compaq CompactFlash sleeve */ + .vendor = 0x1125, + .device = 0xcf11, + }, { /* Compaq single PCMCIA sleeve */ + .vendor = 0x1125, + .device = 0xd7c3, + }, { }, +}; + +static struct ipaq_option_driver ipaq_sleeve_cf = { + .id_table = ipaq_sleeve_cf_ids, + .driver = { + .name = "ipaq-sleeve-cf", + .probe = ipaq_sleeve_cf_probe, + .remove = ipaq_sleeve_cf_remove, + }, +}; + +module_driver(ipaq_sleeve_cf, ipaq_option_driver_register, + ipaq_option_driver_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ipaq, ipaq_sleeve_cf_ids); diff --git a/arch/arm/mach-sa1100/ipaq-sleeve-dual.c b/arch/arm/mach-sa1100/ipaq-sleeve-dual.c new file mode 100644 index 000000000000..dd4c3f756885 --- /dev/null +++ b/arch/arm/mach-sa1100/ipaq-sleeve-dual.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +struct ipaq_sleeve_dual_data { + struct platform_device *skt; +}; + +static int ipaq_sleeve_dual_probe(struct device *dev) +{ + struct ipaq_option_device *idev = to_ipaq_option_device(dev); + struct ipaq_sleeve_dual_data *cf; + struct platform_device_info pinfo = { + .parent = dev, + .name = "sa11x0-pcmcia", + .id = -1, + }; + + cf = devm_kzalloc(dev, sizeof(*cf), GFP_KERNEL); + if (!cf) + return -ENOMEM; + + pinfo.data = &idev->id; + pinfo.size_data = sizeof(idev->id); + + cf->skt = platform_device_register_full(&pinfo); + if (IS_ERR(cf->skt)) { + dev_err(dev, "unable to register pcmcia device: %ld\n", + PTR_ERR(cf->skt)); + return PTR_ERR(cf->skt); + } + + dev_set_drvdata(dev, cf); + + return 0; +} + +static int ipaq_sleeve_dual_remove(struct device *dev) +{ + struct ipaq_sleeve_dual_data *cf = dev_get_drvdata(dev); + + platform_device_unregister(cf->skt); + + return 0; +} + +static const struct ipaq_option_id ipaq_sleeve_dual_ids[] = { + { /* Compaq dual PCMCIA sleeve */ + .vendor = 0x1125, + .device = 0x0001, + }, { }, +}; + +static struct ipaq_option_driver ipaq_sleeve_dual = { + .id_table = ipaq_sleeve_dual_ids, + .driver = { + .name = "ipaq-sleeve-dual", + .probe = ipaq_sleeve_dual_probe, + .remove = ipaq_sleeve_dual_remove, + }, +}; + +module_driver(ipaq_sleeve_dual, ipaq_option_driver_register, + ipaq_option_driver_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ipaq, ipaq_sleeve_dual_ids); diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c index a91222bc3824..a6d387d9ec4d 100644 --- a/drivers/pcmcia/sa1100_h3600.c +++ b/drivers/pcmcia/sa1100_h3600.c @@ -33,24 +33,6 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) switch (skt->nr) { case 0: - err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON"); - if (err) - goto err01; - err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0); - if (err) - goto err03; - err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON"); - if (err) - goto err03; - err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0); - if (err) - goto err04; - err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET"); - if (err) - goto err04; - err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0); - if (err) - goto err05; err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET"); if (err) goto err05; @@ -64,11 +46,7 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) return 0; err06: gpio_free(H3XXX_EGPIO_CARD_RESET); -err05: gpio_free(H3XXX_EGPIO_OPT_RESET); -err04: gpio_free(H3XXX_EGPIO_OPT_ON); -err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); -err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); - return err; +err05: return err; } static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) @@ -76,14 +54,7 @@ static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) switch (skt->nr) { case 0: /* Disable CF bus: */ - gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); - gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); - gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); - gpio_free(H3XXX_EGPIO_CARD_RESET); - gpio_free(H3XXX_EGPIO_OPT_RESET); - gpio_free(H3XXX_EGPIO_OPT_ON); - gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); break; case 1: break; @@ -115,41 +86,12 @@ h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_ return 0; } -static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) -{ - /* Enable CF bus: */ - gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1); - gpio_set_value(H3XXX_EGPIO_OPT_ON, 1); - gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0); - - msleep(10); -} - -static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) -{ - /* - * FIXME: This doesn't fit well. We don't have the mechanism in - * the generic PCMCIA layer to deal with the idea of two sockets - * on one bus. We rely on the cs.c behaviour shutting down - * socket 0 then socket 1. - */ - if (skt->nr == 1) { - gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); - gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); - /* hmm, does this suck power? */ - gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); - } -} - struct pcmcia_low_level h3600_pcmcia_ops = { .owner = THIS_MODULE, .hw_init = h3600_pcmcia_hw_init, .hw_shutdown = h3600_pcmcia_hw_shutdown, .socket_state = h3600_pcmcia_socket_state, .configure_socket = h3600_pcmcia_configure_socket, - - .socket_init = h3600_pcmcia_socket_init, - .socket_suspend = h3600_pcmcia_socket_suspend, }; int pcmcia_h3600_init(struct device *dev) diff --git a/include/linux/ipaq-sleeve.h b/include/linux/ipaq-sleeve.h new file mode 100644 index 000000000000..fb669fad0c4e --- /dev/null +++ b/include/linux/ipaq-sleeve.h @@ -0,0 +1,34 @@ +#ifndef LINUX_IPAQ_SLEEVE_H +#define LINUX_IPAQ_SLEEVE_H + +#include +#include + +struct ipaq_option_device { + struct ipaq_option_id id; + struct device dev; +}; +#define to_ipaq_option_device(x) container_of(x, struct ipaq_option_device, dev) + +struct ipaq_option_driver { + const struct ipaq_option_id *id_table; + struct device_driver driver; +}; + +extern struct bus_type ipaq_option_sleeve_bus; + +int ipaq_option_device_add(struct device *parent, struct ipaq_option_id id); +void ipaq_option_device_del(void); + +static inline int ipaq_option_driver_register(struct ipaq_option_driver *drv) +{ + drv->driver.bus = &ipaq_option_sleeve_bus; + return driver_register(&drv->driver); +} + +static inline void ipaq_option_driver_unregister(struct ipaq_option_driver *drv) +{ + driver_unregister(&drv->driver); +} + +#endif -- cgit