tifm_7xx1.c 11.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 *  tifm_7xx1.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>
#include <linux/dma-mapping.h>
14
#include <linux/freezer.h>
15 16

#define DRIVER_NAME "tifm_7xx1"
17
#define DRIVER_VERSION "0.7"
18 19 20 21 22 23

static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock)
{
	unsigned long flags;

	spin_lock_irqsave(&fm->lock, flags);
24 25
	fm->socket_change_set |= 1 << sock->socket_id;
	wake_up_all(&fm->change_set_notify);
26 27 28
	spin_unlock_irqrestore(&fm->lock, flags);
}

29
static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id)
30 31
{
	struct tifm_adapter *fm = dev_id;
32
	struct tifm_dev *sock;
33 34 35 36 37 38 39 40 41 42 43 44 45
	unsigned int irq_status;
	unsigned int sock_irq_status, cnt;

	spin_lock(&fm->lock);
	irq_status = readl(fm->addr + FM_INTERRUPT_STATUS);
	if (irq_status == 0 || irq_status == (~0)) {
		spin_unlock(&fm->lock);
		return IRQ_NONE;
	}

	if (irq_status & TIFM_IRQ_ENABLE) {
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);

46
		for (cnt = 0; cnt <  fm->num_sockets; cnt++) {
47
			sock = fm->sockets[cnt];
48 49 50
			sock_irq_status = (irq_status >> cnt)
					  & (TIFM_IRQ_FIFOMASK(1)
					     | TIFM_IRQ_CARDMASK(1));
51

52 53
			if (sock && sock_irq_status)
				sock->signal_irq(sock, sock_irq_status);
54
		}
55 56 57

		 fm->socket_change_set |= irq_status
					  & ((1 << fm->num_sockets) - 1);
58 59 60
	}
	writel(irq_status, fm->addr + FM_INTERRUPT_STATUS);

61 62 63 64
	if (!fm->socket_change_set)
		writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE);
	else
		wake_up_all(&fm->change_set_notify);
65 66 67 68 69

	spin_unlock(&fm->lock);
	return IRQ_HANDLED;
}

70
static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, int is_x2)
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 106 107 108 109 110 111 112 113 114
{
	unsigned int s_state;
	int cnt;

	writel(0x0e00, sock_addr + SOCK_CONTROL);

	for (cnt = 0; cnt < 100; cnt++) {
		if (!(TIFM_SOCK_STATE_POWERED &
				readl(sock_addr + SOCK_PRESENT_STATE)))
			break;
		msleep(10);
	}

	s_state = readl(sock_addr + SOCK_PRESENT_STATE);
	if (!(TIFM_SOCK_STATE_OCCUPIED & s_state))
		return FM_NULL;

	if (is_x2) {
		writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL);
	} else {
		// SmartMedia cards need extra 40 msec
		if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1)
			msleep(40);
		writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED,
		       sock_addr + SOCK_CONTROL);
		msleep(10);
		writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED,
			sock_addr + SOCK_CONTROL);
	}

	for (cnt = 0; cnt < 100; cnt++) {
		if ((TIFM_SOCK_STATE_POWERED &
				readl(sock_addr + SOCK_PRESENT_STATE)))
			break;
		msleep(10);
	}

	if (!is_x2)
		writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED),
		       sock_addr + SOCK_CONTROL);

	return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7;
}

115 116
inline static char __iomem *
tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num)
117 118 119 120
{
	return base_addr + ((sock_num + 1) << 10);
}

121
static int tifm_7xx1_switch_media(void *data)
122
{
123
	struct tifm_adapter *fm = data;
124 125 126
	unsigned long flags;
	tifm_media_id media_id;
	char *card_name = "xx";
127
	int cnt, rc;
128 129
	struct tifm_dev *sock;
	unsigned int socket_change_set;
130

131
	while (1) {
132 133 134 135 136
		rc = wait_event_interruptible(fm->change_set_notify,
					      fm->socket_change_set);
		if (rc == -ERESTARTSYS)
			try_to_freeze();

137 138 139 140 141 142 143
		spin_lock_irqsave(&fm->lock, flags);
		socket_change_set = fm->socket_change_set;
		fm->socket_change_set = 0;

		dev_dbg(fm->dev, "checking media set %x\n",
			socket_change_set);

144
		if (kthread_should_stop())
145
			socket_change_set = (1 << fm->num_sockets) - 1;
146 147
		spin_unlock_irqrestore(&fm->lock, flags);

148
		if (!socket_change_set)
149
			continue;
150 151 152 153 154 155 156

		spin_lock_irqsave(&fm->lock, flags);
		for (cnt = 0; cnt < fm->num_sockets; cnt++) {
			if (!(socket_change_set & (1 << cnt)))
				continue;
			sock = fm->sockets[cnt];
			if (sock) {
157
				printk(KERN_INFO DRIVER_NAME
158 159 160 161 162
				       ": demand removing card from socket %d\n",
				       cnt);
				fm->sockets[cnt] = NULL;
				spin_unlock_irqrestore(&fm->lock, flags);
				device_unregister(&sock->dev);
163
				spin_lock_irqsave(&fm->lock, flags);
164 165 166 167
				writel(0x0e00,
				       tifm_7xx1_sock_addr(fm->addr, cnt)
				       + SOCK_CONTROL);
			}
168
			if (kthread_should_stop())
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
				continue;

			spin_unlock_irqrestore(&fm->lock, flags);
			media_id = tifm_7xx1_toggle_sock_power(
					tifm_7xx1_sock_addr(fm->addr, cnt),
					fm->num_sockets == 2);
			if (media_id) {
				sock = tifm_alloc_device(fm);
				if (sock) {
					sock->addr = tifm_7xx1_sock_addr(fm->addr,
									cnt);
					sock->media_id = media_id;
					sock->socket_id = cnt;
					switch (media_id) {
					case 1:
						card_name = "xd";
						break;
					case 2:
						card_name = "ms";
						break;
					case 3:
						card_name = "sd";
						break;
					default:
						tifm_free_device(&sock->dev);
						spin_lock_irqsave(&fm->lock, flags);
						continue;
					}
					snprintf(sock->dev.bus_id, BUS_ID_SIZE,
						"tifm_%s%u:%u", card_name, fm->id, cnt);
					printk(KERN_INFO DRIVER_NAME
						": %s card detected in socket %d\n",
						card_name, cnt);
					if (!device_register(&sock->dev)) {
						spin_lock_irqsave(&fm->lock, flags);
						if (!fm->sockets[cnt]) {
							fm->sockets[cnt] = sock;
							sock = NULL;
						}
						spin_unlock_irqrestore(&fm->lock, flags);
					}
					if (sock)
						tifm_free_device(&sock->dev);
212
				}
213 214 215 216
				spin_lock_irqsave(&fm->lock, flags);
			}
		}

217
		if (!kthread_should_stop()) {
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
			writel(TIFM_IRQ_FIFOMASK(socket_change_set)
			       | TIFM_IRQ_CARDMASK(socket_change_set),
			       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
			writel(TIFM_IRQ_FIFOMASK(socket_change_set)
			       | TIFM_IRQ_CARDMASK(socket_change_set),
			       fm->addr + FM_SET_INTERRUPT_ENABLE);
			writel(TIFM_IRQ_ENABLE,
			       fm->addr + FM_SET_INTERRUPT_ENABLE);
			spin_unlock_irqrestore(&fm->lock, flags);
		} else {
			for (cnt = 0; cnt < fm->num_sockets; cnt++) {
				if (fm->sockets[cnt])
					fm->socket_change_set |= 1 << cnt;
			}
			if (!fm->socket_change_set) {
				spin_unlock_irqrestore(&fm->lock, flags);
234
				return 0;
235
			} else {
236 237 238 239
				spin_unlock_irqrestore(&fm->lock, flags);
			}
		}
	}
240
	return 0;
241 242
}

243 244
#ifdef CONFIG_PM

245 246
static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state)
{
247
	dev_dbg(&dev->dev, "suspending host\n");
248

249 250 251 252
	pci_save_state(dev);
	pci_enable_wake(dev, pci_choose_state(dev, state), 0);
	pci_disable_device(dev);
	pci_set_power_state(dev, pci_choose_state(dev, state));
253 254 255 256 257 258
	return 0;
}

static int tifm_7xx1_resume(struct pci_dev *dev)
{
	struct tifm_adapter *fm = pci_get_drvdata(dev);
259
	int cnt, rc;
260
	unsigned long flags;
261
	tifm_media_id new_ids[fm->num_sockets];
262

263
	pci_set_power_state(dev, PCI_D0);
264
	pci_restore_state(dev);
265 266 267 268 269 270
	rc = pci_enable_device(dev);
	if (rc)
		return rc;
	pci_set_master(dev);

	dev_dbg(&dev->dev, "resuming host\n");
271

272 273 274 275
	for (cnt = 0; cnt < fm->num_sockets; cnt++)
		new_ids[cnt] = tifm_7xx1_toggle_sock_power(
					tifm_7xx1_sock_addr(fm->addr, cnt),
					fm->num_sockets == 2);
276
	spin_lock_irqsave(&fm->lock, flags);
277 278 279 280 281 282 283 284 285 286
	fm->socket_change_set = 0;
	for (cnt = 0; cnt < fm->num_sockets; cnt++) {
		if (fm->sockets[cnt]) {
			if (fm->sockets[cnt]->media_id == new_ids[cnt])
				fm->socket_change_set |= 1 << cnt;

			fm->sockets[cnt]->media_id = new_ids[cnt];
		}
	}

287
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	if (!fm->socket_change_set) {
		spin_unlock_irqrestore(&fm->lock, flags);
		return 0;
	} else {
		fm->socket_change_set = 0;
		spin_unlock_irqrestore(&fm->lock, flags);
	}

	wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ);

	spin_lock_irqsave(&fm->lock, flags);
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_ENABLE,
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	fm->socket_change_set = 0;

310 311 312 313
	spin_unlock_irqrestore(&fm->lock, flags);
	return 0;
}

314 315 316 317 318 319 320
#else

#define tifm_7xx1_suspend NULL
#define tifm_7xx1_resume NULL

#endif /* CONFIG_PM */

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
static int tifm_7xx1_probe(struct pci_dev *dev,
			const struct pci_device_id *dev_id)
{
	struct tifm_adapter *fm;
	int pci_dev_busy = 0;
	int rc;

	rc = pci_set_dma_mask(dev, DMA_32BIT_MASK);
	if (rc)
		return rc;

	rc = pci_enable_device(dev);
	if (rc)
		return rc;

	pci_set_master(dev);

	rc = pci_request_regions(dev, DRIVER_NAME);
	if (rc) {
		pci_dev_busy = 1;
		goto err_out;
	}

	pci_intx(dev, 1);

	fm = tifm_alloc_adapter();
	if (!fm) {
		rc = -ENOMEM;
		goto err_out_int;
	}

	fm->dev = &dev->dev;
353 354
	fm->num_sockets = (dev->device == 0x803B) ? 2 : 4;
	fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets,
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
				GFP_KERNEL);
	if (!fm->sockets)
		goto err_out_free;

	fm->eject = tifm_7xx1_eject;
	pci_set_drvdata(dev, fm);

	fm->addr = ioremap(pci_resource_start(dev, 0),
				pci_resource_len(dev, 0));
	if (!fm->addr)
		goto err_out_free;

	rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm);
	if (rc)
		goto err_out_unmap;

371 372
	init_waitqueue_head(&fm->change_set_notify);
	rc = tifm_add_adapter(fm, tifm_7xx1_switch_media);
373 374 375 376
	if (rc)
		goto err_out_irq;

	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
377
	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
378
		fm->addr + FM_SET_INTERRUPT_ENABLE);
379
	wake_up_process(fm->media_switcher);
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
	return 0;

err_out_irq:
	free_irq(dev->irq, fm);
err_out_unmap:
	iounmap(fm->addr);
err_out_free:
	pci_set_drvdata(dev, NULL);
	tifm_free_adapter(fm);
err_out_int:
	pci_intx(dev, 0);
	pci_release_regions(dev);
err_out:
	if (!pci_dev_busy)
		pci_disable_device(dev);
	return rc;
}

static void tifm_7xx1_remove(struct pci_dev *dev)
{
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	unsigned long flags;

403 404 405 406
	writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	mmiowb();
	free_irq(dev->irq, fm);

407
	spin_lock_irqsave(&fm->lock, flags);
408
	fm->socket_change_set = (1 << fm->num_sockets) - 1;
409 410
	spin_unlock_irqrestore(&fm->lock, flags);

411
	kthread_stop(fm->media_switcher);
412 413 414

	tifm_remove_adapter(fm);

415
	pci_set_drvdata(dev, NULL);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459

	iounmap(fm->addr);
	pci_intx(dev, 0);
	pci_release_regions(dev);

	pci_disable_device(dev);
	tifm_free_adapter(fm);
}

static struct pci_device_id tifm_7xx1_pci_tbl [] = {
	{ PCI_VENDOR_ID_TI, 0x8033, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
	  0 }, /* xx21 - the one I have */
        { PCI_VENDOR_ID_TI, 0x803B, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
	  0 }, /* xx12 - should be also supported */
	{ }
};

static struct pci_driver tifm_7xx1_driver = {
	.name = DRIVER_NAME,
	.id_table = tifm_7xx1_pci_tbl,
	.probe = tifm_7xx1_probe,
	.remove = tifm_7xx1_remove,
	.suspend = tifm_7xx1_suspend,
	.resume = tifm_7xx1_resume,
};

static int __init tifm_7xx1_init(void)
{
	return pci_register_driver(&tifm_7xx1_driver);
}

static void __exit tifm_7xx1_exit(void)
{
	pci_unregister_driver(&tifm_7xx1_driver);
}

MODULE_AUTHOR("Alex Dubov");
MODULE_DESCRIPTION("TI FlashMedia host driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl);
MODULE_VERSION(DRIVER_VERSION);

module_init(tifm_7xx1_init);
module_exit(tifm_7xx1_exit);