| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * linux/amiga/amiflop.c |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 61 | 62 | #include <linux/delay.h> |
|---|
| 62 | 63 | #include <linux/init.h> |
|---|
| 63 | 64 | #include <linux/mutex.h> |
|---|
| 64 | | -#include <linux/amifdreg.h> |
|---|
| 65 | | -#include <linux/amifd.h> |
|---|
| 66 | 65 | #include <linux/fs.h> |
|---|
| 67 | | -#include <linux/blkdev.h> |
|---|
| 66 | +#include <linux/blk-mq.h> |
|---|
| 68 | 67 | #include <linux/elevator.h> |
|---|
| 69 | 68 | #include <linux/interrupt.h> |
|---|
| 70 | 69 | #include <linux/platform_device.h> |
|---|
| .. | .. |
|---|
| 85 | 84 | /* |
|---|
| 86 | 85 | * Defines |
|---|
| 87 | 86 | */ |
|---|
| 87 | + |
|---|
| 88 | +/* |
|---|
| 89 | + * CIAAPRA bits (read only) |
|---|
| 90 | + */ |
|---|
| 91 | + |
|---|
| 92 | +#define DSKRDY (0x1<<5) /* disk ready when low */ |
|---|
| 93 | +#define DSKTRACK0 (0x1<<4) /* head at track zero when low */ |
|---|
| 94 | +#define DSKPROT (0x1<<3) /* disk protected when low */ |
|---|
| 95 | +#define DSKCHANGE (0x1<<2) /* low when disk removed */ |
|---|
| 96 | + |
|---|
| 97 | +/* |
|---|
| 98 | + * CIAAPRB bits (read/write) |
|---|
| 99 | + */ |
|---|
| 100 | + |
|---|
| 101 | +#define DSKMOTOR (0x1<<7) /* motor on when low */ |
|---|
| 102 | +#define DSKSEL3 (0x1<<6) /* select drive 3 when low */ |
|---|
| 103 | +#define DSKSEL2 (0x1<<5) /* select drive 2 when low */ |
|---|
| 104 | +#define DSKSEL1 (0x1<<4) /* select drive 1 when low */ |
|---|
| 105 | +#define DSKSEL0 (0x1<<3) /* select drive 0 when low */ |
|---|
| 106 | +#define DSKSIDE (0x1<<2) /* side selection: 0 = upper, 1 = lower */ |
|---|
| 107 | +#define DSKDIREC (0x1<<1) /* step direction: 0=in, 1=out (to trk 0) */ |
|---|
| 108 | +#define DSKSTEP (0x1) /* pulse low to step head 1 track */ |
|---|
| 109 | + |
|---|
| 110 | +/* |
|---|
| 111 | + * DSKBYTR bits (read only) |
|---|
| 112 | + */ |
|---|
| 113 | + |
|---|
| 114 | +#define DSKBYT (1<<15) /* register contains valid byte when set */ |
|---|
| 115 | +#define DMAON (1<<14) /* disk DMA enabled */ |
|---|
| 116 | +#define DISKWRITE (1<<13) /* disk write bit in DSKLEN enabled */ |
|---|
| 117 | +#define WORDEQUAL (1<<12) /* DSKSYNC register match when true */ |
|---|
| 118 | +/* bits 7-0 are data */ |
|---|
| 119 | + |
|---|
| 120 | +/* |
|---|
| 121 | + * ADKCON/ADKCONR bits |
|---|
| 122 | + */ |
|---|
| 123 | + |
|---|
| 124 | +#ifndef SETCLR |
|---|
| 125 | +#define ADK_SETCLR (1<<15) /* control bit */ |
|---|
| 126 | +#endif |
|---|
| 127 | +#define ADK_PRECOMP1 (1<<14) /* precompensation selection */ |
|---|
| 128 | +#define ADK_PRECOMP0 (1<<13) /* 00=none, 01=140ns, 10=280ns, 11=500ns */ |
|---|
| 129 | +#define ADK_MFMPREC (1<<12) /* 0=GCR precomp., 1=MFM precomp. */ |
|---|
| 130 | +#define ADK_WORDSYNC (1<<10) /* enable DSKSYNC auto DMA */ |
|---|
| 131 | +#define ADK_MSBSYNC (1<<9) /* when 1, enable sync on MSbit (for GCR) */ |
|---|
| 132 | +#define ADK_FAST (1<<8) /* bit cell: 0=2us (GCR), 1=1us (MFM) */ |
|---|
| 133 | + |
|---|
| 134 | +/* |
|---|
| 135 | + * DSKLEN bits |
|---|
| 136 | + */ |
|---|
| 137 | + |
|---|
| 138 | +#define DSKLEN_DMAEN (1<<15) |
|---|
| 139 | +#define DSKLEN_WRITE (1<<14) |
|---|
| 140 | + |
|---|
| 141 | +/* |
|---|
| 142 | + * INTENA/INTREQ bits |
|---|
| 143 | + */ |
|---|
| 144 | + |
|---|
| 145 | +#define DSKINDEX (0x1<<4) /* DSKINDEX bit */ |
|---|
| 146 | + |
|---|
| 147 | +/* |
|---|
| 148 | + * Misc |
|---|
| 149 | + */ |
|---|
| 150 | + |
|---|
| 151 | +#define MFM_SYNC 0x4489 /* standard MFM sync value */ |
|---|
| 152 | + |
|---|
| 153 | +/* Values for FD_COMMAND */ |
|---|
| 154 | +#define FD_RECALIBRATE 0x07 /* move to track 0 */ |
|---|
| 155 | +#define FD_SEEK 0x0F /* seek track */ |
|---|
| 156 | +#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */ |
|---|
| 157 | +#define FD_WRITE 0xC5 /* write with MT, MFM */ |
|---|
| 158 | +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ |
|---|
| 159 | +#define FD_SPECIFY 0x03 /* specify HUT etc */ |
|---|
| 160 | +#define FD_FORMAT 0x4D /* format one track */ |
|---|
| 161 | +#define FD_VERSION 0x10 /* get version code */ |
|---|
| 162 | +#define FD_CONFIGURE 0x13 /* configure FIFO operation */ |
|---|
| 163 | +#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */ |
|---|
| 164 | + |
|---|
| 165 | +#define FD_MAX_UNITS 4 /* Max. Number of drives */ |
|---|
| 166 | +#define FLOPPY_MAX_SECTORS 22 /* Max. Number of sectors per track */ |
|---|
| 167 | + |
|---|
| 168 | +struct fd_data_type { |
|---|
| 169 | + char *name; /* description of data type */ |
|---|
| 170 | + int sects; /* sectors per track */ |
|---|
| 171 | + int (*read_fkt)(int); /* read whole track */ |
|---|
| 172 | + void (*write_fkt)(int); /* write whole track */ |
|---|
| 173 | +}; |
|---|
| 174 | + |
|---|
| 175 | +struct fd_drive_type { |
|---|
| 176 | + unsigned long code; /* code returned from drive */ |
|---|
| 177 | + char *name; /* description of drive */ |
|---|
| 178 | + unsigned int tracks; /* number of tracks */ |
|---|
| 179 | + unsigned int heads; /* number of heads */ |
|---|
| 180 | + unsigned int read_size; /* raw read size for one track */ |
|---|
| 181 | + unsigned int write_size; /* raw write size for one track */ |
|---|
| 182 | + unsigned int sect_mult; /* sectors and gap multiplier (HD = 2) */ |
|---|
| 183 | + unsigned int precomp1; /* start track for precomp 1 */ |
|---|
| 184 | + unsigned int precomp2; /* start track for precomp 2 */ |
|---|
| 185 | + unsigned int step_delay; /* time (in ms) for delay after step */ |
|---|
| 186 | + unsigned int settle_time; /* time to settle after dir change */ |
|---|
| 187 | + unsigned int side_time; /* time needed to change sides */ |
|---|
| 188 | +}; |
|---|
| 189 | + |
|---|
| 190 | +struct amiga_floppy_struct { |
|---|
| 191 | + struct fd_drive_type *type; /* type of floppy for this unit */ |
|---|
| 192 | + struct fd_data_type *dtype; /* type of floppy for this unit */ |
|---|
| 193 | + int track; /* current track (-1 == unknown) */ |
|---|
| 194 | + unsigned char *trackbuf; /* current track (kmaloc()'d */ |
|---|
| 195 | + |
|---|
| 196 | + int blocks; /* total # blocks on disk */ |
|---|
| 197 | + |
|---|
| 198 | + int changed; /* true when not known */ |
|---|
| 199 | + int disk; /* disk in drive (-1 == unknown) */ |
|---|
| 200 | + int motor; /* true when motor is at speed */ |
|---|
| 201 | + int busy; /* true when drive is active */ |
|---|
| 202 | + int dirty; /* true when trackbuf is not on disk */ |
|---|
| 203 | + int status; /* current error code for unit */ |
|---|
| 204 | + struct gendisk *gendisk; |
|---|
| 205 | + struct blk_mq_tag_set tag_set; |
|---|
| 206 | +}; |
|---|
| 88 | 207 | |
|---|
| 89 | 208 | /* |
|---|
| 90 | 209 | * Error codes |
|---|
| .. | .. |
|---|
| 164 | 283 | static int writepending; |
|---|
| 165 | 284 | static int writefromint; |
|---|
| 166 | 285 | static char *raw_buf; |
|---|
| 167 | | -static int fdc_queue; |
|---|
| 168 | 286 | |
|---|
| 169 | 287 | static DEFINE_SPINLOCK(amiflop_lock); |
|---|
| 170 | 288 | |
|---|
| .. | .. |
|---|
| 1337 | 1455 | return -1; |
|---|
| 1338 | 1456 | } |
|---|
| 1339 | 1457 | |
|---|
| 1340 | | -/* |
|---|
| 1341 | | - * Round-robin between our available drives, doing one request from each |
|---|
| 1342 | | - */ |
|---|
| 1343 | | -static struct request *set_next_request(void) |
|---|
| 1458 | +static blk_status_t amiflop_rw_cur_segment(struct amiga_floppy_struct *floppy, |
|---|
| 1459 | + struct request *rq) |
|---|
| 1344 | 1460 | { |
|---|
| 1345 | | - struct request_queue *q; |
|---|
| 1346 | | - int cnt = FD_MAX_UNITS; |
|---|
| 1347 | | - struct request *rq = NULL; |
|---|
| 1348 | | - |
|---|
| 1349 | | - /* Find next queue we can dispatch from */ |
|---|
| 1350 | | - fdc_queue = fdc_queue + 1; |
|---|
| 1351 | | - if (fdc_queue == FD_MAX_UNITS) |
|---|
| 1352 | | - fdc_queue = 0; |
|---|
| 1353 | | - |
|---|
| 1354 | | - for(cnt = FD_MAX_UNITS; cnt > 0; cnt--) { |
|---|
| 1355 | | - |
|---|
| 1356 | | - if (unit[fdc_queue].type->code == FD_NODRIVE) { |
|---|
| 1357 | | - if (++fdc_queue == FD_MAX_UNITS) |
|---|
| 1358 | | - fdc_queue = 0; |
|---|
| 1359 | | - continue; |
|---|
| 1360 | | - } |
|---|
| 1361 | | - |
|---|
| 1362 | | - q = unit[fdc_queue].gendisk->queue; |
|---|
| 1363 | | - if (q) { |
|---|
| 1364 | | - rq = blk_fetch_request(q); |
|---|
| 1365 | | - if (rq) |
|---|
| 1366 | | - break; |
|---|
| 1367 | | - } |
|---|
| 1368 | | - |
|---|
| 1369 | | - if (++fdc_queue == FD_MAX_UNITS) |
|---|
| 1370 | | - fdc_queue = 0; |
|---|
| 1371 | | - } |
|---|
| 1372 | | - |
|---|
| 1373 | | - return rq; |
|---|
| 1374 | | -} |
|---|
| 1375 | | - |
|---|
| 1376 | | -static void redo_fd_request(void) |
|---|
| 1377 | | -{ |
|---|
| 1378 | | - struct request *rq; |
|---|
| 1461 | + int drive = floppy - unit; |
|---|
| 1379 | 1462 | unsigned int cnt, block, track, sector; |
|---|
| 1380 | | - int drive; |
|---|
| 1381 | | - struct amiga_floppy_struct *floppy; |
|---|
| 1382 | 1463 | char *data; |
|---|
| 1383 | | - unsigned long flags; |
|---|
| 1384 | | - blk_status_t err; |
|---|
| 1385 | 1464 | |
|---|
| 1386 | | -next_req: |
|---|
| 1387 | | - rq = set_next_request(); |
|---|
| 1388 | | - if (!rq) { |
|---|
| 1389 | | - /* Nothing left to do */ |
|---|
| 1390 | | - return; |
|---|
| 1391 | | - } |
|---|
| 1392 | | - |
|---|
| 1393 | | - floppy = rq->rq_disk->private_data; |
|---|
| 1394 | | - drive = floppy - unit; |
|---|
| 1395 | | - |
|---|
| 1396 | | -next_segment: |
|---|
| 1397 | | - /* Here someone could investigate to be more efficient */ |
|---|
| 1398 | | - for (cnt = 0, err = BLK_STS_OK; cnt < blk_rq_cur_sectors(rq); cnt++) { |
|---|
| 1465 | + for (cnt = 0; cnt < blk_rq_cur_sectors(rq); cnt++) { |
|---|
| 1399 | 1466 | #ifdef DEBUG |
|---|
| 1400 | 1467 | printk("fd: sector %ld + %d requested for %s\n", |
|---|
| 1401 | 1468 | blk_rq_pos(rq), cnt, |
|---|
| 1402 | 1469 | (rq_data_dir(rq) == READ) ? "read" : "write"); |
|---|
| 1403 | 1470 | #endif |
|---|
| 1404 | 1471 | block = blk_rq_pos(rq) + cnt; |
|---|
| 1405 | | - if ((int)block > floppy->blocks) { |
|---|
| 1406 | | - err = BLK_STS_IOERR; |
|---|
| 1407 | | - break; |
|---|
| 1408 | | - } |
|---|
| 1409 | | - |
|---|
| 1410 | 1472 | track = block / (floppy->dtype->sects * floppy->type->sect_mult); |
|---|
| 1411 | 1473 | sector = block % (floppy->dtype->sects * floppy->type->sect_mult); |
|---|
| 1412 | 1474 | data = bio_data(rq->bio) + 512 * cnt; |
|---|
| .. | .. |
|---|
| 1415 | 1477 | "0x%08lx\n", track, sector, data); |
|---|
| 1416 | 1478 | #endif |
|---|
| 1417 | 1479 | |
|---|
| 1418 | | - if (get_track(drive, track) == -1) { |
|---|
| 1419 | | - err = BLK_STS_IOERR; |
|---|
| 1420 | | - break; |
|---|
| 1421 | | - } |
|---|
| 1480 | + if (get_track(drive, track) == -1) |
|---|
| 1481 | + return BLK_STS_IOERR; |
|---|
| 1422 | 1482 | |
|---|
| 1423 | 1483 | if (rq_data_dir(rq) == READ) { |
|---|
| 1424 | 1484 | memcpy(data, floppy->trackbuf + sector * 512, 512); |
|---|
| .. | .. |
|---|
| 1426 | 1486 | memcpy(floppy->trackbuf + sector * 512, data, 512); |
|---|
| 1427 | 1487 | |
|---|
| 1428 | 1488 | /* keep the drive spinning while writes are scheduled */ |
|---|
| 1429 | | - if (!fd_motor_on(drive)) { |
|---|
| 1430 | | - err = BLK_STS_IOERR; |
|---|
| 1431 | | - break; |
|---|
| 1432 | | - } |
|---|
| 1489 | + if (!fd_motor_on(drive)) |
|---|
| 1490 | + return BLK_STS_IOERR; |
|---|
| 1433 | 1491 | /* |
|---|
| 1434 | 1492 | * setup a callback to write the track buffer |
|---|
| 1435 | 1493 | * after a short (1 tick) delay. |
|---|
| 1436 | 1494 | */ |
|---|
| 1437 | | - local_irq_save(flags); |
|---|
| 1438 | | - |
|---|
| 1439 | 1495 | floppy->dirty = 1; |
|---|
| 1440 | 1496 | /* reset the timer */ |
|---|
| 1441 | 1497 | mod_timer (flush_track_timer + drive, jiffies + 1); |
|---|
| 1442 | | - local_irq_restore(flags); |
|---|
| 1443 | 1498 | } |
|---|
| 1444 | 1499 | } |
|---|
| 1445 | 1500 | |
|---|
| 1446 | | - if (__blk_end_request_cur(rq, err)) |
|---|
| 1447 | | - goto next_segment; |
|---|
| 1448 | | - goto next_req; |
|---|
| 1501 | + return BLK_STS_OK; |
|---|
| 1449 | 1502 | } |
|---|
| 1450 | 1503 | |
|---|
| 1451 | | -static void do_fd_request(struct request_queue * q) |
|---|
| 1504 | +static blk_status_t amiflop_queue_rq(struct blk_mq_hw_ctx *hctx, |
|---|
| 1505 | + const struct blk_mq_queue_data *bd) |
|---|
| 1452 | 1506 | { |
|---|
| 1453 | | - redo_fd_request(); |
|---|
| 1507 | + struct request *rq = bd->rq; |
|---|
| 1508 | + struct amiga_floppy_struct *floppy = rq->rq_disk->private_data; |
|---|
| 1509 | + blk_status_t err; |
|---|
| 1510 | + |
|---|
| 1511 | + if (!spin_trylock_irq(&amiflop_lock)) |
|---|
| 1512 | + return BLK_STS_DEV_RESOURCE; |
|---|
| 1513 | + |
|---|
| 1514 | + blk_mq_start_request(rq); |
|---|
| 1515 | + |
|---|
| 1516 | + do { |
|---|
| 1517 | + err = amiflop_rw_cur_segment(floppy, rq); |
|---|
| 1518 | + } while (blk_update_request(rq, err, blk_rq_cur_bytes(rq))); |
|---|
| 1519 | + blk_mq_end_request(rq, err); |
|---|
| 1520 | + |
|---|
| 1521 | + spin_unlock_irq(&amiflop_lock); |
|---|
| 1522 | + return BLK_STS_OK; |
|---|
| 1454 | 1523 | } |
|---|
| 1455 | 1524 | |
|---|
| 1456 | 1525 | static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
|---|
| .. | .. |
|---|
| 1533 | 1602 | return p->type->read_size; |
|---|
| 1534 | 1603 | #endif |
|---|
| 1535 | 1604 | default: |
|---|
| 1536 | | - printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.", |
|---|
| 1537 | | - cmd, drive); |
|---|
| 1538 | 1605 | return -ENOSYS; |
|---|
| 1539 | 1606 | } |
|---|
| 1540 | 1607 | return 0; |
|---|
| .. | .. |
|---|
| 1603 | 1670 | } |
|---|
| 1604 | 1671 | |
|---|
| 1605 | 1672 | if (mode & (FMODE_READ|FMODE_WRITE)) { |
|---|
| 1606 | | - check_disk_change(bdev); |
|---|
| 1673 | + bdev_check_media_change(bdev); |
|---|
| 1607 | 1674 | if (mode & FMODE_WRITE) { |
|---|
| 1608 | 1675 | int wrprot; |
|---|
| 1609 | 1676 | |
|---|
| .. | .. |
|---|
| 1701 | 1768 | .check_events = amiga_check_events, |
|---|
| 1702 | 1769 | }; |
|---|
| 1703 | 1770 | |
|---|
| 1771 | +static const struct blk_mq_ops amiflop_mq_ops = { |
|---|
| 1772 | + .queue_rq = amiflop_queue_rq, |
|---|
| 1773 | +}; |
|---|
| 1774 | + |
|---|
| 1704 | 1775 | static struct gendisk *fd_alloc_disk(int drive) |
|---|
| 1705 | 1776 | { |
|---|
| 1706 | 1777 | struct gendisk *disk; |
|---|
| .. | .. |
|---|
| 1709 | 1780 | if (!disk) |
|---|
| 1710 | 1781 | goto out; |
|---|
| 1711 | 1782 | |
|---|
| 1712 | | - disk->queue = blk_init_queue(do_fd_request, &amiflop_lock); |
|---|
| 1783 | + disk->queue = blk_mq_init_sq_queue(&unit[drive].tag_set, &amiflop_mq_ops, |
|---|
| 1784 | + 2, BLK_MQ_F_SHOULD_MERGE); |
|---|
| 1713 | 1785 | if (IS_ERR(disk->queue)) { |
|---|
| 1714 | 1786 | disk->queue = NULL; |
|---|
| 1715 | 1787 | goto out_put_disk; |
|---|
| .. | .. |
|---|
| 1724 | 1796 | out_cleanup_queue: |
|---|
| 1725 | 1797 | blk_cleanup_queue(disk->queue); |
|---|
| 1726 | 1798 | disk->queue = NULL; |
|---|
| 1799 | + blk_mq_free_tag_set(&unit[drive].tag_set); |
|---|
| 1727 | 1800 | out_put_disk: |
|---|
| 1728 | 1801 | put_disk(disk); |
|---|
| 1729 | 1802 | out: |
|---|
| .. | .. |
|---|
| 1757 | 1830 | disk->major = FLOPPY_MAJOR; |
|---|
| 1758 | 1831 | disk->first_minor = drive; |
|---|
| 1759 | 1832 | disk->fops = &floppy_fops; |
|---|
| 1833 | + disk->events = DISK_EVENT_MEDIA_CHANGE; |
|---|
| 1760 | 1834 | sprintf(disk->disk_name, "fd%d", drive); |
|---|
| 1761 | 1835 | disk->private_data = &unit[drive]; |
|---|
| 1762 | 1836 | set_capacity(disk, 880*2); |
|---|