From b29cf62c43409fa2f7fbfba4e2ab230a7ac5aff8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 27 Sep 2009 17:30:21 +0100 Subject: ARM: Fix SA1100 Assabet/Neponset PCMCIA section mismatch warnings WARNING: drivers/pcmcia/sa1100_cs.o(.data+0x48): Section mismatch in reference from the variable sa11x0_pcmcia_hw_init to the function .init.text:pcmcia_assabet_init() The variable sa11x0_pcmcia_hw_init references the function __init pcmcia_assabet_init() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/pcmcia/sa1111_cs.o(.text+0x298): Section mismatch in reference from the function pcmcia_probe() to the function .init.text:pcmcia_neponset_init() The function pcmcia_probe() references the function __init pcmcia_neponset_init(). This is often because pcmcia_probe lacks a __init annotation or the annotation of pcmcia_neponset_init is wrong. Signed-off-by: Russell King Acked-by: Sam Ravnborg --- drivers/pcmcia/sa1100_assabet.c | 2 +- drivers/pcmcia/sa1100_neponset.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c index f424146a2bc..ac8aa09ba0d 100644 --- a/drivers/pcmcia/sa1100_assabet.c +++ b/drivers/pcmcia/sa1100_assabet.c @@ -130,7 +130,7 @@ static struct pcmcia_low_level assabet_pcmcia_ops = { .socket_suspend = assabet_pcmcia_socket_suspend, }; -int __init pcmcia_assabet_init(struct device *dev) +int pcmcia_assabet_init(struct device *dev) { int ret = -ENODEV; diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c index 4c41e86ccff..0c76d337815 100644 --- a/drivers/pcmcia/sa1100_neponset.c +++ b/drivers/pcmcia/sa1100_neponset.c @@ -123,7 +123,7 @@ static struct pcmcia_low_level neponset_pcmcia_ops = { .socket_suspend = sa1111_pcmcia_socket_suspend, }; -int __init pcmcia_neponset_init(struct sa1111_dev *sadev) +int pcmcia_neponset_init(struct sa1111_dev *sadev) { int ret = -ENODEV; -- cgit v1.2.3 From 827b4649d4626bf97b203b4bcd69476bb9b4e760 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 29 Sep 2009 00:10:41 +0200 Subject: PM / PCMCIA: Drop second argument of pcmcia_socket_dev_suspend() pcmcia_socket_dev_suspend() doesn't use its second argument, so it may be dropped safely. This change is necessary for the subsequent yenta suspend/resume fix. Signed-off-by: Rafael J. Wysocki Cc: stable@kernel.org --- drivers/pcmcia/at91_cf.c | 2 +- drivers/pcmcia/au1000_generic.c | 2 +- drivers/pcmcia/bfin_cf_pcmcia.c | 2 +- drivers/pcmcia/cs.c | 2 +- drivers/pcmcia/i82092.c | 2 +- drivers/pcmcia/i82365.c | 2 +- drivers/pcmcia/m32r_cfc.c | 2 +- drivers/pcmcia/m32r_pcc.c | 2 +- drivers/pcmcia/m8xx_pcmcia.c | 2 +- drivers/pcmcia/omap_cf.c | 2 +- drivers/pcmcia/pd6729.c | 2 +- drivers/pcmcia/pxa2xx_base.c | 2 +- drivers/pcmcia/sa1100_generic.c | 2 +- drivers/pcmcia/sa1111_generic.c | 2 +- drivers/pcmcia/tcic.c | 2 +- drivers/pcmcia/vrc4171_card.c | 2 +- drivers/pcmcia/yenta_socket.c | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 9e1140f085f..e1dccedc596 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -363,7 +363,7 @@ static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg) struct at91_cf_socket *cf = platform_get_drvdata(pdev); struct at91_cf_data *board = cf->board; - pcmcia_socket_dev_suspend(&pdev->dev, mesg); + pcmcia_socket_dev_suspend(&pdev->dev); if (device_may_wakeup(&pdev->dev)) { enable_irq_wake(board->det_pin); if (board->irq_pin) diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index 90013341cd5..02088704ac2 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -515,7 +515,7 @@ static int au1x00_drv_pcmcia_probe(struct platform_device *dev) static int au1x00_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int au1x00_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index b59d4115d20..300b368605c 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -302,7 +302,7 @@ static int __devexit bfin_cf_remove(struct platform_device *pdev) static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg) { - return pcmcia_socket_dev_suspend(&pdev->dev, mesg); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int bfin_cf_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 0660ad18258..934d4bee39a 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -101,7 +101,7 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem); static int socket_resume(struct pcmcia_socket *skt); static int socket_suspend(struct pcmcia_socket *skt); -int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state) +int pcmcia_socket_dev_suspend(struct device *dev) { struct pcmcia_socket *socket; diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 46561face12..a04f21c8170 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -42,7 +42,7 @@ MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); #ifdef CONFIG_PM static int i82092aa_socket_suspend (struct pci_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int i82092aa_socket_resume (struct pci_dev *dev) diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 40d4953e4b1..b906abe26ad 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -1241,7 +1241,7 @@ static int pcic_init(struct pcmcia_socket *s) static int i82365_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int i82365_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 62b4ecc97c4..d1d89c4491a 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -699,7 +699,7 @@ static struct pccard_operations pcc_operations = { static int cfc_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int cfc_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c index 12034b41d19..a0655839c8d 100644 --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -675,7 +675,7 @@ static struct pccard_operations pcc_operations = { static int pcc_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pcc_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index d1ad0966392..c69f2c4fe52 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1296,7 +1296,7 @@ static int m8xx_remove(struct of_device *ofdev) #ifdef CONFIG_PM static int m8xx_suspend(struct platform_device *pdev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&pdev->dev, state); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int m8xx_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index f3736398900..68570bc3ac8 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -334,7 +334,7 @@ static int __exit omap_cf_remove(struct platform_device *pdev) static int omap_cf_suspend(struct platform_device *pdev, pm_message_t mesg) { - return pcmcia_socket_dev_suspend(&pdev->dev, mesg); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int omap_cf_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 8bed1dab903..1c39d3438f2 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -758,7 +758,7 @@ static void __devexit pd6729_pci_remove(struct pci_dev *dev) #ifdef CONFIG_PM static int pd6729_socket_suspend(struct pci_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pd6729_socket_resume(struct pci_dev *dev) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 87e22ef8eb0..0e35acb1366 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -302,7 +302,7 @@ static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) static int pxa2xx_drv_pcmcia_suspend(struct device *dev) { - return pcmcia_socket_dev_suspend(dev, PMSG_SUSPEND); + return pcmcia_socket_dev_suspend(dev); } static int pxa2xx_drv_pcmcia_resume(struct device *dev) diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index d8da5ac844e..2d0e9975153 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -89,7 +89,7 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev) static int sa11x0_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int sa11x0_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 401052a21ce..4be4e172ffa 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -159,7 +159,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) static int pcmcia_suspend(struct sa1111_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pcmcia_resume(struct sa1111_dev *dev) diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c index 8eb04230fec..582413fcb62 100644 --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -366,7 +366,7 @@ static int __init get_tcic_id(void) static int tcic_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int tcic_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c index d4ad50d737b..c9fcbdc164e 100644 --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -707,7 +707,7 @@ __setup("vrc4171_card=", vrc4171_card_setup); static int vrc4171_card_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int vrc4171_card_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index b459e87a30a..6fa1ed8f2b2 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1230,7 +1230,7 @@ static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state) struct yenta_socket *socket = pci_get_drvdata(dev); int ret; - ret = pcmcia_socket_dev_suspend(&dev->dev, state); + ret = pcmcia_socket_dev_suspend(&dev->dev); if (socket) { if (socket->type && socket->type->save_state) -- cgit v1.2.3 From 0c570cdeb8fdfcb354a3e9cd81bfc6a09c19de0c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 29 Sep 2009 00:11:03 +0200 Subject: PM / yenta: Fix cardbus suspend/resume regression Since 2.6.29 the PCI PM core have been restoring the standard configuration registers of PCI devices in the early phase of resume. In particular, PCI devices without drivers have been handled this way since commit 355a72d75b3b4f4877db4c9070c798238028ecb5 (PCI: Rework default handling of suspend and resume). Unfortunately, this leads to post-resume problems with CardBus devices which cannot be accessed in the early phase of resume, because the sockets they are on have not been woken up yet at that point. To solve this problem, move the yenta socket resume to the early phase of resume and, analogously, move the suspend of it to the late phase of suspend. Additionally, remove some unnecessary PCI code from the yenta socket's resume routine. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13092, which is a post-2.6.28 regression. Signed-off-by: Rafael J. Wysocki Reported-by: Florian Cc: stable@kernel.org --- drivers/pcmcia/yenta_socket.c | 88 +++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 40 deletions(-) (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 6fa1ed8f2b2..abe0e44c6e9 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1225,60 +1225,71 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i } #ifdef CONFIG_PM -static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state) +static int yenta_dev_suspend_noirq(struct device *dev) { - struct yenta_socket *socket = pci_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + struct yenta_socket *socket = pci_get_drvdata(pdev); int ret; - ret = pcmcia_socket_dev_suspend(&dev->dev); + ret = pcmcia_socket_dev_suspend(dev); - if (socket) { - if (socket->type && socket->type->save_state) - socket->type->save_state(socket); + if (!socket) + return ret; - /* FIXME: pci_save_state needs to have a better interface */ - pci_save_state(dev); - pci_read_config_dword(dev, 16*4, &socket->saved_state[0]); - pci_read_config_dword(dev, 17*4, &socket->saved_state[1]); - pci_disable_device(dev); + if (socket->type && socket->type->save_state) + socket->type->save_state(socket); - /* - * Some laptops (IBM T22) do not like us putting the Cardbus - * bridge into D3. At a guess, some other laptop will - * probably require this, so leave it commented out for now. - */ - /* pci_set_power_state(dev, 3); */ - } + pci_save_state(pdev); + pci_read_config_dword(pdev, 16*4, &socket->saved_state[0]); + pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]); + pci_disable_device(pdev); + + /* + * Some laptops (IBM T22) do not like us putting the Cardbus + * bridge into D3. At a guess, some other laptop will + * probably require this, so leave it commented out for now. + */ + /* pci_set_power_state(dev, 3); */ return ret; } - -static int yenta_dev_resume (struct pci_dev *dev) +static int yenta_dev_resume_noirq(struct device *dev) { - struct yenta_socket *socket = pci_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + struct yenta_socket *socket = pci_get_drvdata(pdev); + int ret; - if (socket) { - int rc; + if (!socket) + return 0; - pci_set_power_state(dev, 0); - /* FIXME: pci_restore_state needs to have a better interface */ - pci_restore_state(dev); - pci_write_config_dword(dev, 16*4, socket->saved_state[0]); - pci_write_config_dword(dev, 17*4, socket->saved_state[1]); + pci_write_config_dword(pdev, 16*4, socket->saved_state[0]); + pci_write_config_dword(pdev, 17*4, socket->saved_state[1]); - rc = pci_enable_device(dev); - if (rc) - return rc; + ret = pci_enable_device(pdev); + if (ret) + return ret; - pci_set_master(dev); + pci_set_master(pdev); - if (socket->type && socket->type->restore_state) - socket->type->restore_state(socket); - } + if (socket->type && socket->type->restore_state) + socket->type->restore_state(socket); - return pcmcia_socket_dev_resume(&dev->dev); + return pcmcia_socket_dev_resume(dev); } + +static struct dev_pm_ops yenta_pm_ops = { + .suspend_noirq = yenta_dev_suspend_noirq, + .resume_noirq = yenta_dev_resume_noirq, + .freeze_noirq = yenta_dev_suspend_noirq, + .thaw_noirq = yenta_dev_resume_noirq, + .poweroff_noirq = yenta_dev_suspend_noirq, + .restore_noirq = yenta_dev_resume_noirq, +}; + +#define YENTA_PM_OPS (¥ta_pm_ops) +#else +#define YENTA_PM_OPS NULL #endif #define CB_ID(vend,dev,type) \ @@ -1376,10 +1387,7 @@ static struct pci_driver yenta_cardbus_driver = { .id_table = yenta_table, .probe = yenta_probe, .remove = __devexit_p(yenta_close), -#ifdef CONFIG_PM - .suspend = yenta_dev_suspend, - .resume = yenta_dev_resume, -#endif + .driver.pm = YENTA_PM_OPS, }; -- cgit v1.2.3 From 553d6d5f5b84f11fad8043688137dac96df1a06d Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Mon, 28 Sep 2009 14:49:43 +0200 Subject: MIPS: BCM63xx: Add PCMCIA & Cardbus support. Signed-off-by: Maxime Bizon Reviewed-by: Wolfram Sang Signed-off-by: Ralf Baechle --- drivers/pcmcia/Kconfig | 4 + drivers/pcmcia/Makefile | 1 + drivers/pcmcia/bcm63xx_pcmcia.c | 536 ++++++++++++++++++++++++++++++++++++++++ drivers/pcmcia/bcm63xx_pcmcia.h | 60 +++++ 4 files changed, 601 insertions(+) create mode 100644 drivers/pcmcia/bcm63xx_pcmcia.c create mode 100644 drivers/pcmcia/bcm63xx_pcmcia.h (limited to 'drivers/pcmcia') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index fbf965b31c1..17f38a781d4 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -192,6 +192,10 @@ config PCMCIA_AU1X00 tristate "Au1x00 pcmcia support" depends on SOC_AU1X00 && PCMCIA +config PCMCIA_BCM63XX + tristate "bcm63xx pcmcia support" + depends on BCM63XX && PCMCIA + config PCMCIA_SA1100 tristate "SA1100 support" depends on ARM && ARCH_SA1100 && PCMCIA diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 3247828aa20..a03a38acd77 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o obj-$(CONFIG_M32R_PCC) += m32r_pcc.o obj-$(CONFIG_M32R_CFC) += m32r_cfc.o obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o +obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o obj-$(CONFIG_OMAP_CF) += omap_cf.o diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c new file mode 100644 index 00000000000..bc88a3b19bb --- /dev/null +++ b/drivers/pcmcia/bcm63xx_pcmcia.c @@ -0,0 +1,536 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Maxime Bizon + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "bcm63xx_pcmcia.h" + +#define PFX "bcm63xx_pcmcia: " + +#ifdef CONFIG_CARDBUS +/* if cardbus is used, platform device needs reference to actual pci + * device */ +static struct pci_dev *bcm63xx_cb_dev; +#endif + +/* + * read/write helper for pcmcia regs + */ +static inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off) +{ + return bcm_readl(skt->base + off); +} + +static inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt, + u32 val, u32 off) +{ + bcm_writel(val, skt->base + off); +} + +/* + * This callback should (re-)initialise the socket, turn on status + * interrupts and PCMCIA bus, and wait for power to stabilise so that + * the card status signals report correctly. + * + * Hardware cannot do that. + */ +static int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock) +{ + return 0; +} + +/* + * This callback should remove power on the socket, disable IRQs from + * the card, turn off status interrupts, and disable the PCMCIA bus. + * + * Hardware cannot do that. + */ +static int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock) +{ + return 0; +} + +/* + * Implements the set_socket() operation for the in-kernel PCMCIA + * service (formerly SS_SetSocket in Card Services). We more or + * less punt all of this work and let the kernel handle the details + * of power configuration, reset, &c. We also record the value of + * `state' in order to regurgitate it to the PCMCIA core later. + */ +static int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock, + socket_state_t *state) +{ + struct bcm63xx_pcmcia_socket *skt; + unsigned long flags; + u32 val; + + skt = sock->driver_data; + + spin_lock_irqsave(&skt->lock, flags); + + /* note: hardware cannot control socket power, so we will + * always report SS_POWERON */ + + /* apply socket reset */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + if (state->flags & SS_RESET) + val |= PCMCIA_C1_RESET_MASK; + else + val &= ~PCMCIA_C1_RESET_MASK; + + /* reverse reset logic for cardbus card */ + if (skt->card_detected && (skt->card_type & CARD_CARDBUS)) + val ^= PCMCIA_C1_RESET_MASK; + + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* keep requested state for event reporting */ + skt->requested_state = *state; + + spin_unlock_irqrestore(&skt->lock, flags); + + return 0; +} + +/* + * identity cardtype from VS[12] input, CD[12] input while only VS2 is + * floating, and CD[12] input while only VS1 is floating + */ +enum { + IN_VS1 = (1 << 0), + IN_VS2 = (1 << 1), + IN_CD1_VS2H = (1 << 2), + IN_CD2_VS2H = (1 << 3), + IN_CD1_VS1H = (1 << 4), + IN_CD2_VS1H = (1 << 5), +}; + +static const u8 vscd_to_cardtype[] = { + + /* VS1 float, VS2 float */ + [IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V), + + /* VS1 grounded, VS2 float */ + [IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V), + + /* VS1 grounded, VS2 grounded */ + [0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV), + + /* VS1 tied to CD1, VS2 float */ + [IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V), + + /* VS1 grounded, VS2 tied to CD2 */ + [IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV), + + /* VS1 tied to CD2, VS2 grounded */ + [IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV), + + /* VS1 float, VS2 grounded */ + [IN_VS1] = (CARD_PCCARD | CARD_XV), + + /* VS1 float, VS2 tied to CD2 */ + [IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V), + + /* VS1 float, VS2 tied to CD1 */ + [IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV), + + /* VS1 tied to CD2, VS2 float */ + [IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV), + + /* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */ + [IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */ +}; + +/* + * poll hardware to check card insertion status + */ +static unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt) +{ + unsigned int stat; + u32 val; + + stat = 0; + + /* check CD for card presence */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + + if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK)) + stat |= SS_DETECT; + + /* if new insertion, detect cardtype */ + if ((stat & SS_DETECT) && !skt->card_detected) { + unsigned int stat = 0; + + /* float VS1, float VS2 */ + val |= PCMCIA_C1_VS1OE_MASK; + val |= PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read VS[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0; + stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0; + + /* drive VS1 low, float VS2 */ + val &= ~PCMCIA_C1_VS1OE_MASK; + val |= PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read CD[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0; + stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0; + + /* float VS1, drive VS2 low */ + val |= PCMCIA_C1_VS1OE_MASK; + val &= ~PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read CD[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0; + stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0; + + /* guess cardtype from all this */ + skt->card_type = vscd_to_cardtype[stat]; + if (!skt->card_type) + dev_err(&skt->socket.dev, "unsupported card type\n"); + + /* drive both VS pin to 0 again */ + val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK); + + /* enable correct logic */ + val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK); + if (skt->card_type & CARD_PCCARD) + val |= PCMCIA_C1_EN_PCMCIA_MASK; + else + val |= PCMCIA_C1_EN_CARDBUS_MASK; + + pcmcia_writel(skt, val, PCMCIA_C1_REG); + } + skt->card_detected = (stat & SS_DETECT) ? 1 : 0; + + /* report card type/voltage */ + if (skt->card_type & CARD_CARDBUS) + stat |= SS_CARDBUS; + if (skt->card_type & CARD_3V) + stat |= SS_3VCARD; + if (skt->card_type & CARD_XV) + stat |= SS_XVCARD; + stat |= SS_POWERON; + + if (gpio_get_value(skt->pd->ready_gpio)) + stat |= SS_READY; + + return stat; +} + +/* + * core request to get current socket status + */ +static int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock, + unsigned int *status) +{ + struct bcm63xx_pcmcia_socket *skt; + + skt = sock->driver_data; + + spin_lock_bh(&skt->lock); + *status = __get_socket_status(skt); + spin_unlock_bh(&skt->lock); + + return 0; +} + +/* + * socket polling timer callback + */ +static void bcm63xx_pcmcia_poll(unsigned long data) +{ + struct bcm63xx_pcmcia_socket *skt; + unsigned int stat, events; + + skt = (struct bcm63xx_pcmcia_socket *)data; + + spin_lock_bh(&skt->lock); + + stat = __get_socket_status(skt); + + /* keep only changed bits, and mask with required one from the + * core */ + events = (stat ^ skt->old_status) & skt->requested_state.csc_mask; + skt->old_status = stat; + spin_unlock_bh(&skt->lock); + + if (events) + pcmcia_parse_events(&skt->socket, events); + + mod_timer(&skt->timer, + jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE)); +} + +static int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock, + struct pccard_io_map *map) +{ + /* this doesn't seem to be called by pcmcia layer if static + * mapping is used */ + return 0; +} + +static int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock, + struct pccard_mem_map *map) +{ + struct bcm63xx_pcmcia_socket *skt; + struct resource *res; + + skt = sock->driver_data; + if (map->flags & MAP_ATTRIB) + res = skt->attr_res; + else + res = skt->common_res; + + map->static_start = res->start + map->card_start; + return 0; +} + +static struct pccard_operations bcm63xx_pcmcia_operations = { + .init = bcm63xx_pcmcia_sock_init, + .suspend = bcm63xx_pcmcia_suspend, + .get_status = bcm63xx_pcmcia_get_status, + .set_socket = bcm63xx_pcmcia_set_socket, + .set_io_map = bcm63xx_pcmcia_set_io_map, + .set_mem_map = bcm63xx_pcmcia_set_mem_map, +}; + +/* + * register pcmcia socket to core + */ +static int __devinit bcm63xx_drv_pcmcia_probe(struct platform_device *pdev) +{ + struct bcm63xx_pcmcia_socket *skt; + struct pcmcia_socket *sock; + struct resource *res, *irq_res; + unsigned int regmem_size = 0, iomem_size = 0; + u32 val; + int ret; + + skt = kzalloc(sizeof(*skt), GFP_KERNEL); + if (!skt) + return -ENOMEM; + spin_lock_init(&skt->lock); + sock = &skt->socket; + sock->driver_data = skt; + + /* make sure we have all resources we need */ + skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + skt->pd = pdev->dev.platform_data; + if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) { + ret = -EINVAL; + goto err; + } + + /* remap pcmcia registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regmem_size = resource_size(res); + if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) { + ret = -EINVAL; + goto err; + } + skt->reg_res = res; + + skt->base = ioremap(res->start, regmem_size); + if (!skt->base) { + ret = -ENOMEM; + goto err; + } + + /* remap io registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + iomem_size = resource_size(res); + skt->io_base = ioremap(res->start, iomem_size); + if (!skt->io_base) { + ret = -ENOMEM; + goto err; + } + + /* resources are static */ + sock->resource_ops = &pccard_static_ops; + sock->ops = &bcm63xx_pcmcia_operations; + sock->owner = THIS_MODULE; + sock->dev.parent = &pdev->dev; + sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->io_offset = (unsigned long)skt->io_base; + sock->pci_irq = irq_res->start; + +#ifdef CONFIG_CARDBUS + sock->cb_dev = bcm63xx_cb_dev; + if (bcm63xx_cb_dev) + sock->features |= SS_CAP_CARDBUS; +#endif + + /* assume common & attribute memory have the same size */ + sock->map_size = resource_size(skt->common_res); + + /* initialize polling timer */ + setup_timer(&skt->timer, bcm63xx_pcmcia_poll, (unsigned long)skt); + + /* initialize pcmcia control register, drive VS[12] to 0, + * leave CB IDSEL to the old value since it is set by the PCI + * layer */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + val &= PCMCIA_C1_CBIDSEL_MASK; + val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* + * Hardware has only one set of timings registers, not one for + * each memory access type, so we configure them for the + * slowest one: attribute memory. + */ + val = PCMCIA_C2_DATA16_MASK; + val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT; + val |= 6 << PCMCIA_C2_INACTIVE_SHIFT; + val |= 3 << PCMCIA_C2_SETUP_SHIFT; + val |= 3 << PCMCIA_C2_HOLD_SHIFT; + pcmcia_writel(skt, val, PCMCIA_C2_REG); + + ret = pcmcia_register_socket(sock); + if (ret) + goto err; + + /* start polling socket */ + mod_timer(&skt->timer, + jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE)); + + platform_set_drvdata(pdev, skt); + return 0; + +err: + if (skt->io_base) + iounmap(skt->io_base); + if (skt->base) + iounmap(skt->base); + if (skt->reg_res) + release_mem_region(skt->reg_res->start, regmem_size); + kfree(skt); + return ret; +} + +static int __devexit bcm63xx_drv_pcmcia_remove(struct platform_device *pdev) +{ + struct bcm63xx_pcmcia_socket *skt; + struct resource *res; + + skt = platform_get_drvdata(pdev); + del_timer_sync(&skt->timer); + iounmap(skt->base); + iounmap(skt->io_base); + res = skt->reg_res; + release_mem_region(res->start, resource_size(res)); + kfree(skt); + return 0; +} + +struct platform_driver bcm63xx_pcmcia_driver = { + .probe = bcm63xx_drv_pcmcia_probe, + .remove = __devexit_p(bcm63xx_drv_pcmcia_remove), + .driver = { + .name = "bcm63xx_pcmcia", + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_CARDBUS +static int __devinit bcm63xx_cb_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + /* keep pci device */ + bcm63xx_cb_dev = dev; + return platform_driver_register(&bcm63xx_pcmcia_driver); +} + +static void __devexit bcm63xx_cb_exit(struct pci_dev *dev) +{ + platform_driver_unregister(&bcm63xx_pcmcia_driver); + bcm63xx_cb_dev = NULL; +} + +static struct pci_device_id bcm63xx_cb_table[] = { + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = BCM6348_CPU_ID, + .subvendor = PCI_VENDOR_ID_BROADCOM, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_BRIDGE_CARDBUS << 8, + .class_mask = ~0, + }, + + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = BCM6358_CPU_ID, + .subvendor = PCI_VENDOR_ID_BROADCOM, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_BRIDGE_CARDBUS << 8, + .class_mask = ~0, + }, + + { }, +}; + +MODULE_DEVICE_TABLE(pci, bcm63xx_cb_table); + +static struct pci_driver bcm63xx_cardbus_driver = { + .name = "bcm63xx_cardbus", + .id_table = bcm63xx_cb_table, + .probe = bcm63xx_cb_probe, + .remove = __devexit_p(bcm63xx_cb_exit), +}; +#endif + +/* + * if cardbus support is enabled, register our platform device after + * our fake cardbus bridge has been registered + */ +static int __init bcm63xx_pcmcia_init(void) +{ +#ifdef CONFIG_CARDBUS + return pci_register_driver(&bcm63xx_cardbus_driver); +#else + return platform_driver_register(&bcm63xx_pcmcia_driver); +#endif +} + +static void __exit bcm63xx_pcmcia_exit(void) +{ +#ifdef CONFIG_CARDBUS + return pci_unregister_driver(&bcm63xx_cardbus_driver); +#else + platform_driver_unregister(&bcm63xx_pcmcia_driver); +#endif +} + +module_init(bcm63xx_pcmcia_init); +module_exit(bcm63xx_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxime Bizon "); +MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller"); diff --git a/drivers/pcmcia/bcm63xx_pcmcia.h b/drivers/pcmcia/bcm63xx_pcmcia.h new file mode 100644 index 00000000000..ed957399d86 --- /dev/null +++ b/drivers/pcmcia/bcm63xx_pcmcia.h @@ -0,0 +1,60 @@ +#ifndef BCM63XX_PCMCIA_H_ +#define BCM63XX_PCMCIA_H_ + +#include +#include +#include +#include + +/* socket polling rate in ms */ +#define BCM63XX_PCMCIA_POLL_RATE 500 + +enum { + CARD_CARDBUS = (1 << 0), + CARD_PCCARD = (1 << 1), + CARD_5V = (1 << 2), + CARD_3V = (1 << 3), + CARD_XV = (1 << 4), + CARD_YV = (1 << 5), +}; + +struct bcm63xx_pcmcia_socket { + struct pcmcia_socket socket; + + /* platform specific data */ + struct bcm63xx_pcmcia_platform_data *pd; + + /* all regs access are protected by this spinlock */ + spinlock_t lock; + + /* pcmcia registers resource */ + struct resource *reg_res; + + /* base remapped address of registers */ + void __iomem *base; + + /* whether a card is detected at the moment */ + int card_detected; + + /* type of detected card (mask of above enum) */ + u8 card_type; + + /* keep last socket status to implement event reporting */ + unsigned int old_status; + + /* backup of requested socket state */ + socket_state_t requested_state; + + /* timer used for socket status polling */ + struct timer_list timer; + + /* attribute/common memory resources */ + struct resource *attr_res; + struct resource *common_res; + struct resource *io_res; + + /* base address of io memory */ + void __iomem *io_base; +}; + +#endif /* BCM63XX_PCMCIA_H_ */ -- cgit v1.2.3