xref: /OK3568_Linux_fs/kernel/drivers/usb/host/sl811_cs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * PCMCIA driver for SL811HS (as found in REX-CFU1U)
4*4882a593Smuzhiyun  * Filename: sl811_cs.c
5*4882a593Smuzhiyun  * Author:   Yukio Yamamoto
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Port to sl811-hcd and 2.6.x by
8*4882a593Smuzhiyun  *    Botond Botyanszki <boti@rocketmail.com>
9*4882a593Smuzhiyun  *    Simon Pickering
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  *  Last update: 2005-05-12
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/ptrace.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <linux/string.h>
19*4882a593Smuzhiyun #include <linux/timer.h>
20*4882a593Smuzhiyun #include <linux/ioport.h>
21*4882a593Smuzhiyun #include <linux/platform_device.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include <pcmcia/cistpl.h>
24*4882a593Smuzhiyun #include <pcmcia/cisreg.h>
25*4882a593Smuzhiyun #include <pcmcia/ds.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <linux/usb/sl811.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun MODULE_AUTHOR("Botond Botyanszki");
30*4882a593Smuzhiyun MODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6");
31*4882a593Smuzhiyun MODULE_LICENSE("GPL");
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun /*====================================================================*/
35*4882a593Smuzhiyun /* MACROS                                                             */
36*4882a593Smuzhiyun /*====================================================================*/
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /*====================================================================*/
41*4882a593Smuzhiyun /* VARIABLES                                                          */
42*4882a593Smuzhiyun /*====================================================================*/
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun typedef struct local_info_t {
45*4882a593Smuzhiyun 	struct pcmcia_device	*p_dev;
46*4882a593Smuzhiyun } local_info_t;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static void sl811_cs_release(struct pcmcia_device * link);
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /*====================================================================*/
51*4882a593Smuzhiyun 
release_platform_dev(struct device * dev)52*4882a593Smuzhiyun static void release_platform_dev(struct device * dev)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	dev_dbg(dev, "sl811_cs platform_dev release\n");
55*4882a593Smuzhiyun 	dev->parent = NULL;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun static struct sl811_platform_data platform_data = {
59*4882a593Smuzhiyun 	.potpg		= 100,
60*4882a593Smuzhiyun 	.power		= 50,		/* == 100mA */
61*4882a593Smuzhiyun 	// .reset	= ... FIXME:  invoke CF reset on the card
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun static struct resource resources[] = {
65*4882a593Smuzhiyun 	[0] = {
66*4882a593Smuzhiyun 		.flags	= IORESOURCE_IRQ,
67*4882a593Smuzhiyun 	},
68*4882a593Smuzhiyun 	[1] = {
69*4882a593Smuzhiyun 		// .name   = "address",
70*4882a593Smuzhiyun 		.flags	= IORESOURCE_IO,
71*4882a593Smuzhiyun 	},
72*4882a593Smuzhiyun 	[2] = {
73*4882a593Smuzhiyun 		// .name   = "data",
74*4882a593Smuzhiyun 		.flags	= IORESOURCE_IO,
75*4882a593Smuzhiyun 	},
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun extern struct platform_driver sl811h_driver;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun static struct platform_device platform_dev = {
81*4882a593Smuzhiyun 	.id			= -1,
82*4882a593Smuzhiyun 	.dev = {
83*4882a593Smuzhiyun 		.platform_data = &platform_data,
84*4882a593Smuzhiyun 		.release       = release_platform_dev,
85*4882a593Smuzhiyun 	},
86*4882a593Smuzhiyun 	.resource		= resources,
87*4882a593Smuzhiyun 	.num_resources		= ARRAY_SIZE(resources),
88*4882a593Smuzhiyun };
89*4882a593Smuzhiyun 
sl811_hc_init(struct device * parent,resource_size_t base_addr,int irq)90*4882a593Smuzhiyun static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
91*4882a593Smuzhiyun 			 int irq)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	if (platform_dev.dev.parent)
94*4882a593Smuzhiyun 		return -EBUSY;
95*4882a593Smuzhiyun 	platform_dev.dev.parent = parent;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	/* finish seting up the platform device */
98*4882a593Smuzhiyun 	resources[0].start = irq;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	resources[1].start = base_addr;
101*4882a593Smuzhiyun 	resources[1].end = base_addr;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	resources[2].start = base_addr + 1;
104*4882a593Smuzhiyun 	resources[2].end   = base_addr + 1;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	/* The driver core will probe for us.  We know sl811-hcd has been
107*4882a593Smuzhiyun 	 * initialized already because of the link order dependency created
108*4882a593Smuzhiyun 	 * by referencing "sl811h_driver".
109*4882a593Smuzhiyun 	 */
110*4882a593Smuzhiyun 	platform_dev.name = sl811h_driver.driver.name;
111*4882a593Smuzhiyun 	return platform_device_register(&platform_dev);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /*====================================================================*/
115*4882a593Smuzhiyun 
sl811_cs_detach(struct pcmcia_device * link)116*4882a593Smuzhiyun static void sl811_cs_detach(struct pcmcia_device *link)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	dev_dbg(&link->dev, "sl811_cs_detach\n");
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	sl811_cs_release(link);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	/* This points to the parent local_info_t struct */
123*4882a593Smuzhiyun 	kfree(link->priv);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
sl811_cs_release(struct pcmcia_device * link)126*4882a593Smuzhiyun static void sl811_cs_release(struct pcmcia_device * link)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	dev_dbg(&link->dev, "sl811_cs_release\n");
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	pcmcia_disable_device(link);
131*4882a593Smuzhiyun 	platform_device_unregister(&platform_dev);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
sl811_cs_config_check(struct pcmcia_device * p_dev,void * priv_data)134*4882a593Smuzhiyun static int sl811_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	if (p_dev->config_index == 0)
137*4882a593Smuzhiyun 		return -EINVAL;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	return pcmcia_request_io(p_dev);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 
sl811_cs_config(struct pcmcia_device * link)143*4882a593Smuzhiyun static int sl811_cs_config(struct pcmcia_device *link)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	struct device		*parent = &link->dev;
146*4882a593Smuzhiyun 	int			ret;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	dev_dbg(&link->dev, "sl811_cs_config\n");
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	link->config_flags |= CONF_ENABLE_IRQ |	CONF_AUTO_SET_VPP |
151*4882a593Smuzhiyun 		CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (pcmcia_loop_config(link, sl811_cs_config_check, NULL))
154*4882a593Smuzhiyun 		goto failed;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	/* require an IRQ and two registers */
157*4882a593Smuzhiyun 	if (resource_size(link->resource[0]) < 2)
158*4882a593Smuzhiyun 		goto failed;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	if (!link->irq)
161*4882a593Smuzhiyun 		goto failed;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	ret = pcmcia_enable_device(link);
164*4882a593Smuzhiyun 	if (ret)
165*4882a593Smuzhiyun 		goto failed;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	if (sl811_hc_init(parent, link->resource[0]->start, link->irq)
168*4882a593Smuzhiyun 			< 0) {
169*4882a593Smuzhiyun failed:
170*4882a593Smuzhiyun 		printk(KERN_WARNING "sl811_cs_config failed\n");
171*4882a593Smuzhiyun 		sl811_cs_release(link);
172*4882a593Smuzhiyun 		return  -ENODEV;
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 	return 0;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
sl811_cs_probe(struct pcmcia_device * link)177*4882a593Smuzhiyun static int sl811_cs_probe(struct pcmcia_device *link)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	local_info_t *local;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
182*4882a593Smuzhiyun 	if (!local)
183*4882a593Smuzhiyun 		return -ENOMEM;
184*4882a593Smuzhiyun 	local->p_dev = link;
185*4882a593Smuzhiyun 	link->priv = local;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	return sl811_cs_config(link);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun static const struct pcmcia_device_id sl811_ids[] = {
191*4882a593Smuzhiyun 	PCMCIA_DEVICE_MANF_CARD(0xc015, 0x0001), /* RATOC USB HOST CF+ Card */
192*4882a593Smuzhiyun 	PCMCIA_DEVICE_NULL,
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pcmcia, sl811_ids);
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun static struct pcmcia_driver sl811_cs_driver = {
197*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
198*4882a593Smuzhiyun 	.name		= "sl811_cs",
199*4882a593Smuzhiyun 	.probe		= sl811_cs_probe,
200*4882a593Smuzhiyun 	.remove		= sl811_cs_detach,
201*4882a593Smuzhiyun 	.id_table	= sl811_ids,
202*4882a593Smuzhiyun };
203*4882a593Smuzhiyun module_pcmcia_driver(sl811_cs_driver);
204