11da177e4SLinus Torvalds /*======================================================================
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds Device driver for the PCMCIA control functionality of StrongARM
41da177e4SLinus Torvalds SA-1100 microprocessors.
51da177e4SLinus Torvalds
61da177e4SLinus Torvalds The contents of this file are subject to the Mozilla Public
71da177e4SLinus Torvalds License Version 1.1 (the "License"); you may not use this file
81da177e4SLinus Torvalds except in compliance with the License. You may obtain a copy of
91da177e4SLinus Torvalds the License at http://www.mozilla.org/MPL/
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds Software distributed under the License is distributed on an "AS
121da177e4SLinus Torvalds IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
131da177e4SLinus Torvalds implied. See the License for the specific language governing
141da177e4SLinus Torvalds rights and limitations under the License.
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds The initial developer of the original code is John G. Dorsey
171da177e4SLinus Torvalds <john+@cs.cmu.edu>. Portions created by John G. Dorsey are
181da177e4SLinus Torvalds Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
191da177e4SLinus Torvalds
201da177e4SLinus Torvalds Alternatively, the contents of this file may be used under the
211da177e4SLinus Torvalds terms of the GNU Public License version 2 (the "GPL"), in which
221da177e4SLinus Torvalds case the provisions of the GPL are applicable instead of the
231da177e4SLinus Torvalds above. If you wish to allow the use of your version of this file
241da177e4SLinus Torvalds only under the terms of the GPL and not to allow others to use
251da177e4SLinus Torvalds your version of this file under the MPL, indicate your decision
261da177e4SLinus Torvalds by deleting the provisions above and replace them with the notice
271da177e4SLinus Torvalds and other provisions required by the GPL. If you do not delete
281da177e4SLinus Torvalds the provisions above, a recipient may use your version of this
291da177e4SLinus Torvalds file under either the MPL or the GPL.
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds ======================================================================*/
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds #include <linux/module.h>
347be893aaSRussell King #include <linux/gpio/consumer.h>
351da177e4SLinus Torvalds #include <linux/init.h>
367be893aaSRussell King #include <linux/regulator/consumer.h>
375a0e3ad6STejun Heo #include <linux/slab.h>
38d052d1beSRussell King #include <linux/platform_device.h>
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds #include <pcmcia/ss.h>
411da177e4SLinus Torvalds
4277bb86a1SPavel Machek #include <asm/hardware/scoop.h>
4377bb86a1SPavel Machek
441da177e4SLinus Torvalds #include "sa1100_generic.h"
451da177e4SLinus Torvalds
467be893aaSRussell King static const char *sa11x0_cf_gpio_names[] = {
477be893aaSRussell King [SOC_STAT_CD] = "detect",
487be893aaSRussell King [SOC_STAT_BVD1] = "bvd1",
497be893aaSRussell King [SOC_STAT_BVD2] = "bvd2",
507be893aaSRussell King [SOC_STAT_RDY] = "ready",
517be893aaSRussell King };
527be893aaSRussell King
sa11x0_cf_hw_init(struct soc_pcmcia_socket * skt)537be893aaSRussell King static int sa11x0_cf_hw_init(struct soc_pcmcia_socket *skt)
547be893aaSRussell King {
557be893aaSRussell King struct device *dev = skt->socket.dev.parent;
567be893aaSRussell King int i;
577be893aaSRussell King
587be893aaSRussell King skt->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
597be893aaSRussell King if (IS_ERR(skt->gpio_reset))
607be893aaSRussell King return PTR_ERR(skt->gpio_reset);
617be893aaSRussell King
627be893aaSRussell King skt->gpio_bus_enable = devm_gpiod_get_optional(dev, "bus-enable",
637be893aaSRussell King GPIOD_OUT_HIGH);
647be893aaSRussell King if (IS_ERR(skt->gpio_bus_enable))
657be893aaSRussell King return PTR_ERR(skt->gpio_bus_enable);
667be893aaSRussell King
677be893aaSRussell King skt->vcc.reg = devm_regulator_get_optional(dev, "vcc");
687be893aaSRussell King if (IS_ERR(skt->vcc.reg))
697be893aaSRussell King return PTR_ERR(skt->vcc.reg);
707be893aaSRussell King
717be893aaSRussell King if (!skt->vcc.reg)
727be893aaSRussell King dev_warn(dev,
737be893aaSRussell King "no Vcc regulator provided, ignoring Vcc controls\n");
747be893aaSRussell King
757be893aaSRussell King for (i = 0; i < ARRAY_SIZE(sa11x0_cf_gpio_names); i++) {
767be893aaSRussell King skt->stat[i].name = sa11x0_cf_gpio_names[i];
777be893aaSRussell King skt->stat[i].desc = devm_gpiod_get_optional(dev,
787be893aaSRussell King sa11x0_cf_gpio_names[i], GPIOD_IN);
797be893aaSRussell King if (IS_ERR(skt->stat[i].desc))
807be893aaSRussell King return PTR_ERR(skt->stat[i].desc);
817be893aaSRussell King }
827be893aaSRussell King return 0;
837be893aaSRussell King }
847be893aaSRussell King
sa11x0_cf_configure_socket(struct soc_pcmcia_socket * skt,const socket_state_t * state)857be893aaSRussell King static int sa11x0_cf_configure_socket(struct soc_pcmcia_socket *skt,
867be893aaSRussell King const socket_state_t *state)
877be893aaSRussell King {
887be893aaSRussell King return soc_pcmcia_regulator_set(skt, &skt->vcc, state->Vcc);
897be893aaSRussell King }
907be893aaSRussell King
917be893aaSRussell King static struct pcmcia_low_level sa11x0_cf_ops = {
927be893aaSRussell King .owner = THIS_MODULE,
937be893aaSRussell King .hw_init = sa11x0_cf_hw_init,
947be893aaSRussell King .socket_state = soc_common_cf_socket_state,
957be893aaSRussell King .configure_socket = sa11x0_cf_configure_socket,
967be893aaSRussell King };
977be893aaSRussell King
9877bb86a1SPavel Machek int __init pcmcia_collie_init(struct device *dev);
9977bb86a1SPavel Machek
1007be893aaSRussell King static int (*sa11x0_pcmcia_legacy_hw_init[])(struct device *dev) = {
101*9a9e1be1SArnd Bergmann #ifdef CONFIG_SA1100_H3600
1021da177e4SLinus Torvalds pcmcia_h3600_init,
1031da177e4SLinus Torvalds #endif
10477bb86a1SPavel Machek #ifdef CONFIG_SA1100_COLLIE
10577bb86a1SPavel Machek pcmcia_collie_init,
10677bb86a1SPavel Machek #endif
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds
sa11x0_drv_pcmcia_legacy_probe(struct platform_device * dev)1097be893aaSRussell King static int sa11x0_drv_pcmcia_legacy_probe(struct platform_device *dev)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds int i, ret = -ENODEV;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds /*
1141da177e4SLinus Torvalds * Initialise any "on-board" PCMCIA sockets.
1151da177e4SLinus Torvalds */
1167be893aaSRussell King for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_legacy_hw_init); i++) {
1177be893aaSRussell King ret = sa11x0_pcmcia_legacy_hw_init[i](&dev->dev);
1181da177e4SLinus Torvalds if (ret == 0)
1191da177e4SLinus Torvalds break;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds return ret;
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds
sa11x0_drv_pcmcia_legacy_remove(struct platform_device * dev)12509b2c10dSUwe Kleine-König static void sa11x0_drv_pcmcia_legacy_remove(struct platform_device *dev)
1267a192ec3SMing Lei {
127be85458eSRussell King - ARM Linux struct skt_dev_info *sinfo = platform_get_drvdata(dev);
128be85458eSRussell King - ARM Linux int i;
129be85458eSRussell King - ARM Linux
130be85458eSRussell King - ARM Linux platform_set_drvdata(dev, NULL);
131be85458eSRussell King - ARM Linux
132be85458eSRussell King - ARM Linux for (i = 0; i < sinfo->nskt; i++)
133be85458eSRussell King - ARM Linux soc_pcmcia_remove_one(&sinfo->skt[i]);
1347a192ec3SMing Lei }
1357a192ec3SMing Lei
sa11x0_drv_pcmcia_probe(struct platform_device * pdev)1367be893aaSRussell King static int sa11x0_drv_pcmcia_probe(struct platform_device *pdev)
1377be893aaSRussell King {
1387be893aaSRussell King struct soc_pcmcia_socket *skt;
1397be893aaSRussell King struct device *dev = &pdev->dev;
1407be893aaSRussell King
1417be893aaSRussell King if (pdev->id == -1)
1427be893aaSRussell King return sa11x0_drv_pcmcia_legacy_probe(pdev);
1437be893aaSRussell King
1447be893aaSRussell King skt = devm_kzalloc(dev, sizeof(*skt), GFP_KERNEL);
1457be893aaSRussell King if (!skt)
1467be893aaSRussell King return -ENOMEM;
1477be893aaSRussell King
1487be893aaSRussell King platform_set_drvdata(pdev, skt);
1497be893aaSRussell King
1507be893aaSRussell King skt->nr = pdev->id;
1517be893aaSRussell King skt->clk = devm_clk_get(dev, NULL);
1527be893aaSRussell King if (IS_ERR(skt->clk))
1537be893aaSRussell King return PTR_ERR(skt->clk);
1547be893aaSRussell King
1557be893aaSRussell King sa11xx_drv_pcmcia_ops(&sa11x0_cf_ops);
1567be893aaSRussell King soc_pcmcia_init_one(skt, &sa11x0_cf_ops, dev);
1577be893aaSRussell King
1587be893aaSRussell King return sa11xx_drv_pcmcia_add_one(skt);
1597be893aaSRussell King }
1607be893aaSRussell King
sa11x0_drv_pcmcia_remove(struct platform_device * dev)1617be893aaSRussell King static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
1627be893aaSRussell King {
1637be893aaSRussell King struct soc_pcmcia_socket *skt;
1647be893aaSRussell King
16509b2c10dSUwe Kleine-König if (dev->id == -1) {
16609b2c10dSUwe Kleine-König sa11x0_drv_pcmcia_legacy_remove(dev);
16709b2c10dSUwe Kleine-König return 0;
16809b2c10dSUwe Kleine-König }
1697be893aaSRussell King
1707be893aaSRussell King skt = platform_get_drvdata(dev);
1717be893aaSRussell King
1727be893aaSRussell King soc_pcmcia_remove_one(skt);
1737be893aaSRussell King
1747be893aaSRussell King return 0;
1757be893aaSRussell King }
1767be893aaSRussell King
1777a192ec3SMing Lei static struct platform_driver sa11x0_pcmcia_driver = {
1787a192ec3SMing Lei .driver = {
1791da177e4SLinus Torvalds .name = "sa11x0-pcmcia",
1807a192ec3SMing Lei },
1817a192ec3SMing Lei .probe = sa11x0_drv_pcmcia_probe,
1827a192ec3SMing Lei .remove = sa11x0_drv_pcmcia_remove,
1831da177e4SLinus Torvalds };
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds /* sa11x0_pcmcia_init()
1861da177e4SLinus Torvalds * ^^^^^^^^^^^^^^^^^^^^
1871da177e4SLinus Torvalds *
1881da177e4SLinus Torvalds * This routine performs low-level PCMCIA initialization and then
1891da177e4SLinus Torvalds * registers this socket driver with Card Services.
1901da177e4SLinus Torvalds *
1911da177e4SLinus Torvalds * Returns: 0 on success, -ve error code on failure
1921da177e4SLinus Torvalds */
sa11x0_pcmcia_init(void)1931da177e4SLinus Torvalds static int __init sa11x0_pcmcia_init(void)
1941da177e4SLinus Torvalds {
1957a192ec3SMing Lei return platform_driver_register(&sa11x0_pcmcia_driver);
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds /* sa11x0_pcmcia_exit()
1991da177e4SLinus Torvalds * ^^^^^^^^^^^^^^^^^^^^
2001da177e4SLinus Torvalds * Invokes the low-level kernel service to free IRQs associated with this
2011da177e4SLinus Torvalds * socket controller and reset GPIO edge detection.
2021da177e4SLinus Torvalds */
sa11x0_pcmcia_exit(void)2031da177e4SLinus Torvalds static void __exit sa11x0_pcmcia_exit(void)
2041da177e4SLinus Torvalds {
2057a192ec3SMing Lei platform_driver_unregister(&sa11x0_pcmcia_driver);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds
2081da177e4SLinus Torvalds MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
2091da177e4SLinus Torvalds MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11x0 Socket Controller");
2101da177e4SLinus Torvalds MODULE_LICENSE("Dual MPL/GPL");
2111da177e4SLinus Torvalds
212f36598aeSRichard Purdie fs_initcall(sa11x0_pcmcia_init);
2131da177e4SLinus Torvalds module_exit(sa11x0_pcmcia_exit);
214