diff options
| author | Philippe De Muyter <phdm@macqel.be> | 2009-02-18 17:48:36 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-02-18 18:37:55 -0500 |
| commit | 5a74db06cc8d36a325913aa4968ae169f997a466 (patch) | |
| tree | 68eb369ba49b4c764d4900ae41b5115c615a8a83 | |
| parent | ffa7525c13eb3db0fd19a3e1cffe2ce6f561f5f3 (diff) | |
floppy: request and release only the ports we actually use
The floppy driver requests an I/O port it doesn't need, and sometimes this
causes a conflict with a motherboard device reported by PNPBIOS.
This patch makes the floppy driver request and release only the ports it
actually uses. It also factors out the request/release stuff and the
io-ports list so they're all in one place now.
The current floppy driver uses only these ports:
0x3f2 (FD_DOR)
0x3f4 (FD_STATUS)
0x3f5 (FD_DATA)
0x3f7 (FD_DCR/FD_DIR)
but it requests 0x3f2-0x3f5 and 0x3f7, which includes the unused port
0x3f3.
Some BIOSes report 0x3f3 as a motherboard resource. The PNP system driver
reserves that, which causes a conflict when the floppy driver requests
0x3f2-0x3f5 later.
Philippe reported that this conflict broke the floppy driver between
2.6.11 and 2.6.22. His PNPBIOS reports these devices:
$ cat 00:07/id 00:07/resources # motherboard device
PNP0c02
state = active
io 0x80-0x80
io 0x10-0x1f
io 0x22-0x3f
io 0x44-0x5f
io 0x90-0x9f
io 0xa2-0xbf
io 0x3f0-0x3f1
io 0x3f3-0x3f3
$ cat 00:03/id 00:03/resources # floppy device
PNP0700
state = active
io 0x3f4-0x3f5
io 0x3f2-0x3f2
Reference:
http://lkml.org/lkml/2009/1/31/162
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Philippe De Muyter <phdm@macqel.be>
Reported-by: Philippe De Muyter <phdm@macqel.be>
Tested-by: Philippe De Muyter <phdm@macqel.be>
Cc: Adam M Belay <abelay@mit.edu>
Cc: Robert Hancock <hancockrwd@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | drivers/block/floppy.c | 79 |
1 files changed, 52 insertions, 27 deletions
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index cf29cc4e6ab7..83d8ed39433d 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c | |||
| @@ -558,6 +558,8 @@ static void process_fd_request(void); | |||
| 558 | static void recalibrate_floppy(void); | 558 | static void recalibrate_floppy(void); |
| 559 | static void floppy_shutdown(unsigned long); | 559 | static void floppy_shutdown(unsigned long); |
| 560 | 560 | ||
| 561 | static int floppy_request_regions(int); | ||
| 562 | static void floppy_release_regions(int); | ||
| 561 | static int floppy_grab_irq_and_dma(void); | 563 | static int floppy_grab_irq_and_dma(void); |
| 562 | static void floppy_release_irq_and_dma(void); | 564 | static void floppy_release_irq_and_dma(void); |
| 563 | 565 | ||
| @@ -4274,8 +4276,7 @@ static int __init floppy_init(void) | |||
| 4274 | FDCS->rawcmd = 2; | 4276 | FDCS->rawcmd = 2; |
| 4275 | if (user_reset_fdc(-1, FD_RESET_ALWAYS, 0)) { | 4277 | if (user_reset_fdc(-1, FD_RESET_ALWAYS, 0)) { |
| 4276 | /* free ioports reserved by floppy_grab_irq_and_dma() */ | 4278 | /* free ioports reserved by floppy_grab_irq_and_dma() */ |
| 4277 | release_region(FDCS->address + 2, 4); | 4279 | floppy_release_regions(fdc); |
| 4278 | release_region(FDCS->address + 7, 1); | ||
| 4279 | FDCS->address = -1; | 4280 | FDCS->address = -1; |
| 4280 | FDCS->version = FDC_NONE; | 4281 | FDCS->version = FDC_NONE; |
| 4281 | continue; | 4282 | continue; |
| @@ -4284,8 +4285,7 @@ static int __init floppy_init(void) | |||
| 4284 | FDCS->version = get_fdc_version(); | 4285 | FDCS->version = get_fdc_version(); |
| 4285 | if (FDCS->version == FDC_NONE) { | 4286 | if (FDCS->version == FDC_NONE) { |
| 4286 | /* free ioports reserved by floppy_grab_irq_and_dma() */ | 4287 | /* free ioports reserved by floppy_grab_irq_and_dma() */ |
| 4287 | release_region(FDCS->address + 2, 4); | 4288 | floppy_release_regions(fdc); |
| 4288 | release_region(FDCS->address + 7, 1); | ||
| 4289 | FDCS->address = -1; | 4289 | FDCS->address = -1; |
| 4290 | continue; | 4290 | continue; |
| 4291 | } | 4291 | } |
| @@ -4358,6 +4358,47 @@ out_put_disk: | |||
| 4358 | 4358 | ||
| 4359 | static DEFINE_SPINLOCK(floppy_usage_lock); | 4359 | static DEFINE_SPINLOCK(floppy_usage_lock); |
| 4360 | 4360 | ||
| 4361 | static const struct io_region { | ||
| 4362 | int offset; | ||
| 4363 | int size; | ||
| 4364 | } io_regions[] = { | ||
| 4365 | { 2, 1 }, | ||
| 4366 | /* address + 3 is sometimes reserved by pnp bios for motherboard */ | ||
| 4367 | { 4, 2 }, | ||
| 4368 | /* address + 6 is reserved, and may be taken by IDE. | ||
| 4369 | * Unfortunately, Adaptec doesn't know this :-(, */ | ||
| 4370 | { 7, 1 }, | ||
| 4371 | }; | ||
| 4372 | |||
| 4373 | static void floppy_release_allocated_regions(int fdc, const struct io_region *p) | ||
| 4374 | { | ||
| 4375 | while (p != io_regions) { | ||
| 4376 | p--; | ||
| 4377 | release_region(FDCS->address + p->offset, p->size); | ||
| 4378 | } | ||
| 4379 | } | ||
| 4380 | |||
| 4381 | #define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) | ||
| 4382 | |||
| 4383 | static int floppy_request_regions(int fdc) | ||
| 4384 | { | ||
| 4385 | const struct io_region *p; | ||
| 4386 | |||
| 4387 | for (p = io_regions; p < ARRAY_END(io_regions); p++) { | ||
| 4388 | if (!request_region(FDCS->address + p->offset, p->size, "floppy")) { | ||
| 4389 | DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + p->offset); | ||
| 4390 | floppy_release_allocated_regions(fdc, p); | ||
| 4391 | return -EBUSY; | ||
| 4392 | } | ||
| 4393 | } | ||
| 4394 | return 0; | ||
| 4395 | } | ||
| 4396 | |||
| 4397 | static void floppy_release_regions(int fdc) | ||
| 4398 | { | ||
| 4399 | floppy_release_allocated_regions(fdc, ARRAY_END(io_regions)); | ||
| 4400 | } | ||
| 4401 | |||
| 4361 | static int floppy_grab_irq_and_dma(void) | 4402 | static int floppy_grab_irq_and_dma(void) |
| 4362 | { | 4403 | { |
| 4363 | unsigned long flags; | 4404 | unsigned long flags; |
| @@ -4399,18 +4440,8 @@ static int floppy_grab_irq_and_dma(void) | |||
| 4399 | 4440 | ||
| 4400 | for (fdc = 0; fdc < N_FDC; fdc++) { | 4441 | for (fdc = 0; fdc < N_FDC; fdc++) { |
| 4401 | if (FDCS->address != -1) { | 4442 | if (FDCS->address != -1) { |
| 4402 | if (!request_region(FDCS->address + 2, 4, "floppy")) { | 4443 | if (floppy_request_regions(fdc)) |
| 4403 | DPRINT("Floppy io-port 0x%04lx in use\n", | 4444 | goto cleanup; |
| 4404 | FDCS->address + 2); | ||
| 4405 | goto cleanup1; | ||
| 4406 | } | ||
| 4407 | if (!request_region(FDCS->address + 7, 1, "floppy DIR")) { | ||
| 4408 | DPRINT("Floppy io-port 0x%04lx in use\n", | ||
| 4409 | FDCS->address + 7); | ||
| 4410 | goto cleanup2; | ||
| 4411 | } | ||
| 4412 | /* address + 6 is reserved, and may be taken by IDE. | ||
| 4413 | * Unfortunately, Adaptec doesn't know this :-(, */ | ||
| 4414 | } | 4445 | } |
| 4415 | } | 4446 | } |
| 4416 | for (fdc = 0; fdc < N_FDC; fdc++) { | 4447 | for (fdc = 0; fdc < N_FDC; fdc++) { |
| @@ -4432,15 +4463,11 @@ static int floppy_grab_irq_and_dma(void) | |||
| 4432 | fdc = 0; | 4463 | fdc = 0; |
| 4433 | irqdma_allocated = 1; | 4464 | irqdma_allocated = 1; |
| 4434 | return 0; | 4465 | return 0; |
| 4435 | cleanup2: | 4466 | cleanup: |
| 4436 | release_region(FDCS->address + 2, 4); | ||
| 4437 | cleanup1: | ||
| 4438 | fd_free_irq(); | 4467 | fd_free_irq(); |
| 4439 | fd_free_dma(); | 4468 | fd_free_dma(); |
| 4440 | while (--fdc >= 0) { | 4469 | while (--fdc >= 0) |
| 4441 | release_region(FDCS->address + 2, 4); | 4470 | floppy_release_regions(fdc); |
| 4442 | release_region(FDCS->address + 7, 1); | ||
| 4443 | } | ||
| 4444 | spin_lock_irqsave(&floppy_usage_lock, flags); | 4471 | spin_lock_irqsave(&floppy_usage_lock, flags); |
| 4445 | usage_count--; | 4472 | usage_count--; |
| 4446 | spin_unlock_irqrestore(&floppy_usage_lock, flags); | 4473 | spin_unlock_irqrestore(&floppy_usage_lock, flags); |
| @@ -4501,10 +4528,8 @@ static void floppy_release_irq_and_dma(void) | |||
| 4501 | #endif | 4528 | #endif |
| 4502 | old_fdc = fdc; | 4529 | old_fdc = fdc; |
| 4503 | for (fdc = 0; fdc < N_FDC; fdc++) | 4530 | for (fdc = 0; fdc < N_FDC; fdc++) |
| 4504 | if (FDCS->address != -1) { | 4531 | if (FDCS->address != -1) |
| 4505 | release_region(FDCS->address + 2, 4); | 4532 | floppy_release_regions(fdc); |
| 4506 | release_region(FDCS->address + 7, 1); | ||
| 4507 | } | ||
| 4508 | fdc = old_fdc; | 4533 | fdc = old_fdc; |
| 4509 | } | 4534 | } |
| 4510 | 4535 | ||
