Blame view

drivers/misc/tifm_core.c 8.21 KB
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   *  tifm_core.c - TI FlashMedia driver
   *
   *  Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/tifm.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
14
15
  #include <linux/init.h>
  #include <linux/idr.h>
eb12a679b   Paul Gortmaker   drivers/misc: Add...
16
  #include <linux/module.h>
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
17
18
  
  #define DRIVER_NAME "tifm_core"
4552f0cbd   Alex Dubov   tifm: hide detail...
19
  #define DRIVER_VERSION "0.8"
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
20

3540af8ff   Alex Dubov   tifm: replace per...
21
  static struct workqueue_struct *workqueue;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
22
23
  static DEFINE_IDR(tifm_adapter_idr);
  static DEFINE_SPINLOCK(tifm_adapter_lock);
e23f2b8a1   Alex Dubov   tifm: simplify bu...
24
  static const char *tifm_media_type_name(unsigned char type, unsigned char nt)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
25
  {
e23f2b8a1   Alex Dubov   tifm: simplify bu...
26
27
28
29
30
31
32
33
34
  	const char *card_type_name[3][3] = {
  		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
  		{ "XD", "MS", "SD"},
  		{ "xd", "ms", "sd"}
  	};
  
  	if (nt > 2 || type < 1 || type > 3)
  		return NULL;
  	return card_type_name[nt][type - 1];
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
35
  }
e23f2b8a1   Alex Dubov   tifm: simplify bu...
36
  static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
37
  {
e23f2b8a1   Alex Dubov   tifm: simplify bu...
38
  	if (sock->type == id->type)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
39
  		return 1;
e23f2b8a1   Alex Dubov   tifm: simplify bu...
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  	return 0;
  }
  
  static int tifm_bus_match(struct device *dev, struct device_driver *drv)
  {
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
  	struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver,
  						  driver);
  	struct tifm_device_id *ids = fm_drv->id_table;
  
  	if (ids) {
  		while (ids->type) {
  			if (tifm_dev_match(sock, ids))
  				return 1;
  			++ids;
  		}
  	}
  	return 0;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
58
  }
7eff2e7a8   Kay Sievers   Driver core: chan...
59
  static int tifm_uevent(struct device *dev, struct kobj_uevent_env *env)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
60
  {
e23f2b8a1   Alex Dubov   tifm: simplify bu...
61
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
62

7eff2e7a8   Kay Sievers   Driver core: chan...
63
  	if (add_uevent_var(env, "TIFM_CARD_TYPE=%s", tifm_media_type_name(sock->type, 1)))
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
64
65
66
67
  		return -ENOMEM;
  
  	return 0;
  }
8dc4a61ec   Alex Dubov   tifm: use bus met...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  static int tifm_device_probe(struct device *dev)
  {
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
  	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
  					       driver);
  	int rc = -ENODEV;
  
  	get_device(dev);
  	if (dev->driver && drv->probe) {
  		rc = drv->probe(sock);
  		if (!rc)
  			return 0;
  	}
  	put_device(dev);
  	return rc;
  }
  
  static void tifm_dummy_event(struct tifm_dev *sock)
  {
  	return;
  }
  
  static int tifm_device_remove(struct device *dev)
  {
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
  	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
  					       driver);
  
  	if (dev->driver && drv->remove) {
  		sock->card_event = tifm_dummy_event;
  		sock->data_event = tifm_dummy_event;
  		drv->remove(sock);
  		sock->dev.driver = NULL;
  	}
  
  	put_device(dev);
  	return 0;
  }
41d78f740   Alex Dubov   tifm_core: add su...
106
107
108
109
  #ifdef CONFIG_PM
  
  static int tifm_device_suspend(struct device *dev, pm_message_t state)
  {
91f8d0118   Alex Dubov   tifm: layout fixe...
110
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
8dc4a61ec   Alex Dubov   tifm: use bus met...
111
112
  	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
  					       driver);
41d78f740   Alex Dubov   tifm_core: add su...
113

8dc4a61ec   Alex Dubov   tifm: use bus met...
114
  	if (dev->driver && drv->suspend)
91f8d0118   Alex Dubov   tifm: layout fixe...
115
  		return drv->suspend(sock, state);
41d78f740   Alex Dubov   tifm_core: add su...
116
117
118
119
120
  	return 0;
  }
  
  static int tifm_device_resume(struct device *dev)
  {
91f8d0118   Alex Dubov   tifm: layout fixe...
121
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
8dc4a61ec   Alex Dubov   tifm: use bus met...
122
123
  	struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
  					       driver);
41d78f740   Alex Dubov   tifm_core: add su...
124

8dc4a61ec   Alex Dubov   tifm: use bus met...
125
  	if (dev->driver && drv->resume)
91f8d0118   Alex Dubov   tifm: layout fixe...
126
  		return drv->resume(sock);
41d78f740   Alex Dubov   tifm_core: add su...
127
128
129
130
131
132
133
134
135
  	return 0;
  }
  
  #else
  
  #define tifm_device_suspend NULL
  #define tifm_device_resume NULL
  
  #endif /* CONFIG_PM */
4e64f2238   Alex Dubov   tifm: add sysfs a...
136
137
138
139
140
141
142
143
144
145
146
  static ssize_t type_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
  {
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
  	return sprintf(buf, "%x", sock->type);
  }
  
  static struct device_attribute tifm_dev_attrs[] = {
  	__ATTR(type, S_IRUGO, type_show, NULL),
  	__ATTR_NULL
  };
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
147
  static struct bus_type tifm_bus_type = {
91f8d0118   Alex Dubov   tifm: layout fixe...
148
149
150
151
152
153
154
155
  	.name      = "tifm",
  	.dev_attrs = tifm_dev_attrs,
  	.match     = tifm_bus_match,
  	.uevent    = tifm_uevent,
  	.probe     = tifm_device_probe,
  	.remove    = tifm_device_remove,
  	.suspend   = tifm_device_suspend,
  	.resume    = tifm_device_resume
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
156
  };
7dd817d08   Tony Jones   tifm: Convert fro...
157
  static void tifm_free(struct device *dev)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
158
  {
7dd817d08   Tony Jones   tifm: Convert fro...
159
  	struct tifm_adapter *fm = container_of(dev, struct tifm_adapter, dev);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
160

4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
161
162
163
164
165
  	kfree(fm);
  }
  
  static struct class tifm_adapter_class = {
  	.name    = "tifm_adapter",
7dd817d08   Tony Jones   tifm: Convert fro...
166
  	.dev_release = tifm_free
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
167
  };
6113ed73e   Alex Dubov   tifm: move common...
168
169
  struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets,
  					struct device *dev)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
170
171
  {
  	struct tifm_adapter *fm;
6113ed73e   Alex Dubov   tifm: move common...
172
173
  	fm = kzalloc(sizeof(struct tifm_adapter)
  		     + sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
174
  	if (fm) {
7dd817d08   Tony Jones   tifm: Convert fro...
175
176
177
  		fm->dev.class = &tifm_adapter_class;
  		fm->dev.parent = dev;
  		device_initialize(&fm->dev);
6113ed73e   Alex Dubov   tifm: move common...
178
179
  		spin_lock_init(&fm->lock);
  		fm->num_sockets = num_sockets;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
180
181
182
183
  	}
  	return fm;
  }
  EXPORT_SYMBOL(tifm_alloc_adapter);
3540af8ff   Alex Dubov   tifm: replace per...
184
  int tifm_add_adapter(struct tifm_adapter *fm)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
185
186
187
188
189
190
191
192
193
  {
  	int rc;
  
  	if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL))
  		return -ENOMEM;
  
  	spin_lock(&tifm_adapter_lock);
  	rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id);
  	spin_unlock(&tifm_adapter_lock);
6113ed73e   Alex Dubov   tifm: move common...
194
195
  	if (rc)
  		return rc;
0bad16aa0   Kay Sievers   tifm: struct devi...
196
  	dev_set_name(&fm->dev, "tifm%u", fm->id);
7dd817d08   Tony Jones   tifm: Convert fro...
197
  	rc = device_add(&fm->dev);
6113ed73e   Alex Dubov   tifm: move common...
198
199
200
201
  	if (rc) {
  		spin_lock(&tifm_adapter_lock);
  		idr_remove(&tifm_adapter_idr, fm->id);
  		spin_unlock(&tifm_adapter_lock);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
202
  	}
6113ed73e   Alex Dubov   tifm: move common...
203

4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
204
205
206
207
208
209
  	return rc;
  }
  EXPORT_SYMBOL(tifm_add_adapter);
  
  void tifm_remove_adapter(struct tifm_adapter *fm)
  {
6113ed73e   Alex Dubov   tifm: move common...
210
  	unsigned int cnt;
3540af8ff   Alex Dubov   tifm: replace per...
211
  	flush_workqueue(workqueue);
6113ed73e   Alex Dubov   tifm: move common...
212
213
214
215
  	for (cnt = 0; cnt < fm->num_sockets; ++cnt) {
  		if (fm->sockets[cnt])
  			device_unregister(&fm->sockets[cnt]->dev);
  	}
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
216
217
218
219
  
  	spin_lock(&tifm_adapter_lock);
  	idr_remove(&tifm_adapter_idr, fm->id);
  	spin_unlock(&tifm_adapter_lock);
7dd817d08   Tony Jones   tifm: Convert fro...
220
  	device_del(&fm->dev);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
221
222
  }
  EXPORT_SYMBOL(tifm_remove_adapter);
6113ed73e   Alex Dubov   tifm: move common...
223
224
  void tifm_free_adapter(struct tifm_adapter *fm)
  {
7dd817d08   Tony Jones   tifm: Convert fro...
225
  	put_device(&fm->dev);
6113ed73e   Alex Dubov   tifm: move common...
226
227
  }
  EXPORT_SYMBOL(tifm_free_adapter);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
228
229
  void tifm_free_device(struct device *dev)
  {
2428a8fe2   Alex Dubov   tifm: move common...
230
231
  	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
  	kfree(sock);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
232
233
  }
  EXPORT_SYMBOL(tifm_free_device);
2428a8fe2   Alex Dubov   tifm: move common...
234
235
  struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
  				   unsigned char type)
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
236
  {
2428a8fe2   Alex Dubov   tifm: move common...
237
238
239
240
  	struct tifm_dev *sock = NULL;
  
  	if (!tifm_media_type_name(type, 0))
  		return sock;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
241

2428a8fe2   Alex Dubov   tifm: move common...
242
243
244
245
246
247
248
  	sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);
  	if (sock) {
  		spin_lock_init(&sock->lock);
  		sock->type = type;
  		sock->socket_id = id;
  		sock->card_event = tifm_dummy_event;
  		sock->data_event = tifm_dummy_event;
8e02f8581   Alex Dubov   tifm_sd: restruct...
249

7dd817d08   Tony Jones   tifm: Convert fro...
250
  		sock->dev.parent = fm->dev.parent;
2428a8fe2   Alex Dubov   tifm: move common...
251
  		sock->dev.bus = &tifm_bus_type;
7dd817d08   Tony Jones   tifm: Convert fro...
252
  		sock->dev.dma_mask = fm->dev.parent->dma_mask;
2428a8fe2   Alex Dubov   tifm: move common...
253
  		sock->dev.release = tifm_free_device;
0bad16aa0   Kay Sievers   tifm: struct devi...
254
255
  		dev_set_name(&sock->dev, "tifm_%s%u:%u",
  			     tifm_media_type_name(type, 2), fm->id, id);
2428a8fe2   Alex Dubov   tifm: move common...
256
257
258
259
  		printk(KERN_INFO DRIVER_NAME
  		       ": %s card detected in socket %u:%u
  ",
  		       tifm_media_type_name(type, 0), fm->id, id);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
260
  	}
2428a8fe2   Alex Dubov   tifm: move common...
261
  	return sock;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
262
263
264
265
266
267
268
269
270
  }
  EXPORT_SYMBOL(tifm_alloc_device);
  
  void tifm_eject(struct tifm_dev *sock)
  {
  	struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
  	fm->eject(fm, sock);
  }
  EXPORT_SYMBOL(tifm_eject);
baf8532a1   Alex Dubov   memstick: initial...
271
272
273
274
275
276
  int tifm_has_ms_pif(struct tifm_dev *sock)
  {
  	struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
  	return fm->has_ms_pif(fm, sock);
  }
  EXPORT_SYMBOL(tifm_has_ms_pif);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
277
278
279
280
281
282
283
284
285
286
287
288
289
  int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
  		int direction)
  {
  	return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction);
  }
  EXPORT_SYMBOL(tifm_map_sg);
  
  void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
  		   int direction)
  {
  	pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction);
  }
  EXPORT_SYMBOL(tifm_unmap_sg);
3540af8ff   Alex Dubov   tifm: replace per...
290
291
292
293
294
  void tifm_queue_work(struct work_struct *work)
  {
  	queue_work(workqueue, work);
  }
  EXPORT_SYMBOL(tifm_queue_work);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
295
296
297
  int tifm_register_driver(struct tifm_driver *drv)
  {
  	drv->driver.bus = &tifm_bus_type;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
298
299
300
301
302
303
304
305
306
307
308
309
310
  
  	return driver_register(&drv->driver);
  }
  EXPORT_SYMBOL(tifm_register_driver);
  
  void tifm_unregister_driver(struct tifm_driver *drv)
  {
  	driver_unregister(&drv->driver);
  }
  EXPORT_SYMBOL(tifm_unregister_driver);
  
  static int __init tifm_init(void)
  {
3540af8ff   Alex Dubov   tifm: replace per...
311
  	int rc;
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
312

58a69cb47   Tejun Heo   workqueue, freeze...
313
  	workqueue = create_freezable_workqueue("tifm");
3540af8ff   Alex Dubov   tifm: replace per...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  	if (!workqueue)
  		return -ENOMEM;
  
  	rc = bus_register(&tifm_bus_type);
  
  	if (rc)
  		goto err_out_wq;
  
  	rc = class_register(&tifm_adapter_class);
  	if (!rc)
  		return 0;
  
  	bus_unregister(&tifm_bus_type);
  
  err_out_wq:
  	destroy_workqueue(workqueue);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
330
331
332
333
334
335
336
337
  
  	return rc;
  }
  
  static void __exit tifm_exit(void)
  {
  	class_unregister(&tifm_adapter_class);
  	bus_unregister(&tifm_bus_type);
3540af8ff   Alex Dubov   tifm: replace per...
338
  	destroy_workqueue(workqueue);
4020f2d7f   Alex Dubov   [PATCH] mmc: driv...
339
340
341
342
343
344
345
346
347
348
  }
  
  subsys_initcall(tifm_init);
  module_exit(tifm_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Alex Dubov");
  MODULE_DESCRIPTION("TI FlashMedia core driver");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(DRIVER_VERSION);