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