.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Xilinx SystemACE device driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2007 Secret Lab Technologies Ltd. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms of the GNU General Public License version 2 as published |
---|
8 | | - * by the Free Software Foundation. |
---|
9 | 6 | */ |
---|
10 | 7 | |
---|
11 | 8 | /* |
---|
.. | .. |
---|
88 | 85 | #include <linux/kernel.h> |
---|
89 | 86 | #include <linux/delay.h> |
---|
90 | 87 | #include <linux/slab.h> |
---|
91 | | -#include <linux/blkdev.h> |
---|
| 88 | +#include <linux/blk-mq.h> |
---|
92 | 89 | #include <linux/mutex.h> |
---|
93 | 90 | #include <linux/ata.h> |
---|
94 | 91 | #include <linux/hdreg.h> |
---|
.. | .. |
---|
209 | 206 | struct device *dev; |
---|
210 | 207 | struct request_queue *queue; |
---|
211 | 208 | struct gendisk *gd; |
---|
| 209 | + struct blk_mq_tag_set tag_set; |
---|
| 210 | + struct list_head rq_list; |
---|
212 | 211 | |
---|
213 | 212 | /* Inserted CF card parameters */ |
---|
214 | 213 | u16 cf_id[ATA_ID_WORDS]; |
---|
.. | .. |
---|
444 | 443 | #define ACE_FSM_NUM_STATES 11 |
---|
445 | 444 | |
---|
446 | 445 | /* Set flag to exit FSM loop and reschedule tasklet */ |
---|
447 | | -static inline void ace_fsm_yield(struct ace_device *ace) |
---|
| 446 | +static inline void ace_fsm_yieldpoll(struct ace_device *ace) |
---|
448 | 447 | { |
---|
449 | | - dev_dbg(ace->dev, "ace_fsm_yield()\n"); |
---|
450 | 448 | tasklet_schedule(&ace->fsm_tasklet); |
---|
451 | 449 | ace->fsm_continue_flag = 0; |
---|
| 450 | +} |
---|
| 451 | + |
---|
| 452 | +static inline void ace_fsm_yield(struct ace_device *ace) |
---|
| 453 | +{ |
---|
| 454 | + dev_dbg(ace->dev, "%s()\n", __func__); |
---|
| 455 | + ace_fsm_yieldpoll(ace); |
---|
452 | 456 | } |
---|
453 | 457 | |
---|
454 | 458 | /* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */ |
---|
.. | .. |
---|
456 | 460 | { |
---|
457 | 461 | dev_dbg(ace->dev, "ace_fsm_yieldirq()\n"); |
---|
458 | 462 | |
---|
459 | | - if (!ace->irq) |
---|
460 | | - /* No IRQ assigned, so need to poll */ |
---|
461 | | - tasklet_schedule(&ace->fsm_tasklet); |
---|
462 | | - ace->fsm_continue_flag = 0; |
---|
| 463 | + if (ace->irq > 0) |
---|
| 464 | + ace->fsm_continue_flag = 0; |
---|
| 465 | + else |
---|
| 466 | + ace_fsm_yieldpoll(ace); |
---|
| 467 | +} |
---|
| 468 | + |
---|
| 469 | +static bool ace_has_next_request(struct request_queue *q) |
---|
| 470 | +{ |
---|
| 471 | + struct ace_device *ace = q->queuedata; |
---|
| 472 | + |
---|
| 473 | + return !list_empty(&ace->rq_list); |
---|
463 | 474 | } |
---|
464 | 475 | |
---|
465 | 476 | /* Get the next read/write request; ending requests that we don't handle */ |
---|
466 | 477 | static struct request *ace_get_next_request(struct request_queue *q) |
---|
467 | 478 | { |
---|
468 | | - struct request *req; |
---|
| 479 | + struct ace_device *ace = q->queuedata; |
---|
| 480 | + struct request *rq; |
---|
469 | 481 | |
---|
470 | | - while ((req = blk_peek_request(q)) != NULL) { |
---|
471 | | - if (!blk_rq_is_passthrough(req)) |
---|
472 | | - break; |
---|
473 | | - blk_start_request(req); |
---|
474 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
---|
| 482 | + rq = list_first_entry_or_null(&ace->rq_list, struct request, queuelist); |
---|
| 483 | + if (rq) { |
---|
| 484 | + list_del_init(&rq->queuelist); |
---|
| 485 | + blk_mq_start_request(rq); |
---|
475 | 486 | } |
---|
476 | | - return req; |
---|
| 487 | + |
---|
| 488 | + return NULL; |
---|
477 | 489 | } |
---|
478 | 490 | |
---|
479 | 491 | static void ace_fsm_dostate(struct ace_device *ace) |
---|
.. | .. |
---|
499 | 511 | |
---|
500 | 512 | /* Drop all in-flight and pending requests */ |
---|
501 | 513 | if (ace->req) { |
---|
502 | | - __blk_end_request_all(ace->req, BLK_STS_IOERR); |
---|
| 514 | + blk_mq_end_request(ace->req, BLK_STS_IOERR); |
---|
503 | 515 | ace->req = NULL; |
---|
504 | 516 | } |
---|
505 | | - while ((req = blk_fetch_request(ace->queue)) != NULL) |
---|
506 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
---|
| 517 | + while ((req = ace_get_next_request(ace->queue)) != NULL) |
---|
| 518 | + blk_mq_end_request(req, BLK_STS_IOERR); |
---|
507 | 519 | |
---|
508 | 520 | /* Drop back to IDLE state and notify waiters */ |
---|
509 | 521 | ace->fsm_state = ACE_FSM_STATE_IDLE; |
---|
.. | .. |
---|
517 | 529 | switch (ace->fsm_state) { |
---|
518 | 530 | case ACE_FSM_STATE_IDLE: |
---|
519 | 531 | /* See if there is anything to do */ |
---|
520 | | - if (ace->id_req_count || ace_get_next_request(ace->queue)) { |
---|
| 532 | + if (ace->id_req_count || ace_has_next_request(ace->queue)) { |
---|
521 | 533 | ace->fsm_iter_num++; |
---|
522 | 534 | ace->fsm_state = ACE_FSM_STATE_REQ_LOCK; |
---|
523 | 535 | mod_timer(&ace->stall_timer, jiffies + HZ); |
---|
.. | .. |
---|
651 | 663 | ace->fsm_state = ACE_FSM_STATE_IDLE; |
---|
652 | 664 | break; |
---|
653 | 665 | } |
---|
654 | | - blk_start_request(req); |
---|
655 | 666 | |
---|
656 | 667 | /* Okay, it's a data request, set it up for transfer */ |
---|
657 | 668 | dev_dbg(ace->dev, |
---|
.. | .. |
---|
728 | 739 | } |
---|
729 | 740 | |
---|
730 | 741 | /* bio finished; is there another one? */ |
---|
731 | | - if (__blk_end_request_cur(ace->req, BLK_STS_OK)) { |
---|
| 742 | + if (blk_update_request(ace->req, BLK_STS_OK, |
---|
| 743 | + blk_rq_cur_bytes(ace->req))) { |
---|
732 | 744 | /* dev_dbg(ace->dev, "next block; h=%u c=%u\n", |
---|
733 | 745 | * blk_rq_sectors(ace->req), |
---|
734 | 746 | * blk_rq_cur_sectors(ace->req)); |
---|
.. | .. |
---|
854 | 866 | /* --------------------------------------------------------------------- |
---|
855 | 867 | * Block ops |
---|
856 | 868 | */ |
---|
857 | | -static void ace_request(struct request_queue * q) |
---|
| 869 | +static blk_status_t ace_queue_rq(struct blk_mq_hw_ctx *hctx, |
---|
| 870 | + const struct blk_mq_queue_data *bd) |
---|
858 | 871 | { |
---|
859 | | - struct request *req; |
---|
860 | | - struct ace_device *ace; |
---|
| 872 | + struct ace_device *ace = hctx->queue->queuedata; |
---|
| 873 | + struct request *req = bd->rq; |
---|
861 | 874 | |
---|
862 | | - req = ace_get_next_request(q); |
---|
863 | | - |
---|
864 | | - if (req) { |
---|
865 | | - ace = req->rq_disk->private_data; |
---|
866 | | - tasklet_schedule(&ace->fsm_tasklet); |
---|
| 875 | + if (blk_rq_is_passthrough(req)) { |
---|
| 876 | + blk_mq_start_request(req); |
---|
| 877 | + return BLK_STS_IOERR; |
---|
867 | 878 | } |
---|
| 879 | + |
---|
| 880 | + spin_lock_irq(&ace->lock); |
---|
| 881 | + list_add_tail(&req->queuelist, &ace->rq_list); |
---|
| 882 | + spin_unlock_irq(&ace->lock); |
---|
| 883 | + |
---|
| 884 | + tasklet_schedule(&ace->fsm_tasklet); |
---|
| 885 | + return BLK_STS_OK; |
---|
868 | 886 | } |
---|
869 | 887 | |
---|
870 | 888 | static unsigned int ace_check_events(struct gendisk *gd, unsigned int clearing) |
---|
.. | .. |
---|
875 | 893 | return ace->media_change ? DISK_EVENT_MEDIA_CHANGE : 0; |
---|
876 | 894 | } |
---|
877 | 895 | |
---|
878 | | -static int ace_revalidate_disk(struct gendisk *gd) |
---|
| 896 | +static void ace_media_changed(struct ace_device *ace) |
---|
879 | 897 | { |
---|
880 | | - struct ace_device *ace = gd->private_data; |
---|
881 | 898 | unsigned long flags; |
---|
882 | 899 | |
---|
883 | | - dev_dbg(ace->dev, "ace_revalidate_disk()\n"); |
---|
| 900 | + dev_dbg(ace->dev, "requesting cf id and scheduling tasklet\n"); |
---|
884 | 901 | |
---|
885 | | - if (ace->media_change) { |
---|
886 | | - dev_dbg(ace->dev, "requesting cf id and scheduling tasklet\n"); |
---|
| 902 | + spin_lock_irqsave(&ace->lock, flags); |
---|
| 903 | + ace->id_req_count++; |
---|
| 904 | + spin_unlock_irqrestore(&ace->lock, flags); |
---|
887 | 905 | |
---|
888 | | - spin_lock_irqsave(&ace->lock, flags); |
---|
889 | | - ace->id_req_count++; |
---|
890 | | - spin_unlock_irqrestore(&ace->lock, flags); |
---|
891 | | - |
---|
892 | | - tasklet_schedule(&ace->fsm_tasklet); |
---|
893 | | - wait_for_completion(&ace->id_completion); |
---|
894 | | - } |
---|
| 906 | + tasklet_schedule(&ace->fsm_tasklet); |
---|
| 907 | + wait_for_completion(&ace->id_completion); |
---|
895 | 908 | |
---|
896 | 909 | dev_dbg(ace->dev, "revalidate complete\n"); |
---|
897 | | - return ace->id_result; |
---|
898 | 910 | } |
---|
899 | 911 | |
---|
900 | 912 | static int ace_open(struct block_device *bdev, fmode_t mode) |
---|
.. | .. |
---|
909 | 921 | ace->users++; |
---|
910 | 922 | spin_unlock_irqrestore(&ace->lock, flags); |
---|
911 | 923 | |
---|
912 | | - check_disk_change(bdev); |
---|
| 924 | + if (bdev_check_media_change(bdev) && ace->media_change) |
---|
| 925 | + ace_media_changed(ace); |
---|
913 | 926 | mutex_unlock(&xsysace_mutex); |
---|
914 | 927 | |
---|
915 | 928 | return 0; |
---|
.. | .. |
---|
953 | 966 | .open = ace_open, |
---|
954 | 967 | .release = ace_release, |
---|
955 | 968 | .check_events = ace_check_events, |
---|
956 | | - .revalidate_disk = ace_revalidate_disk, |
---|
957 | 969 | .getgeo = ace_getgeo, |
---|
| 970 | +}; |
---|
| 971 | + |
---|
| 972 | +static const struct blk_mq_ops ace_mq_ops = { |
---|
| 973 | + .queue_rq = ace_queue_rq, |
---|
958 | 974 | }; |
---|
959 | 975 | |
---|
960 | 976 | /* -------------------------------------------------------------------- |
---|
.. | .. |
---|
972 | 988 | |
---|
973 | 989 | spin_lock_init(&ace->lock); |
---|
974 | 990 | init_completion(&ace->id_completion); |
---|
| 991 | + INIT_LIST_HEAD(&ace->rq_list); |
---|
975 | 992 | |
---|
976 | 993 | /* |
---|
977 | 994 | * Map the device |
---|
.. | .. |
---|
989 | 1006 | /* |
---|
990 | 1007 | * Initialize the request queue |
---|
991 | 1008 | */ |
---|
992 | | - ace->queue = blk_init_queue(ace_request, &ace->lock); |
---|
993 | | - if (ace->queue == NULL) |
---|
| 1009 | + ace->queue = blk_mq_init_sq_queue(&ace->tag_set, &ace_mq_ops, 2, |
---|
| 1010 | + BLK_MQ_F_SHOULD_MERGE); |
---|
| 1011 | + if (IS_ERR(ace->queue)) { |
---|
| 1012 | + rc = PTR_ERR(ace->queue); |
---|
| 1013 | + ace->queue = NULL; |
---|
994 | 1014 | goto err_blk_initq; |
---|
| 1015 | + } |
---|
| 1016 | + ace->queue->queuedata = ace; |
---|
| 1017 | + |
---|
995 | 1018 | blk_queue_logical_block_size(ace->queue, 512); |
---|
996 | 1019 | blk_queue_bounce_limit(ace->queue, BLK_BOUNCE_HIGH); |
---|
997 | 1020 | |
---|
.. | .. |
---|
1005 | 1028 | ace->gd->major = ace_major; |
---|
1006 | 1029 | ace->gd->first_minor = ace->id * ACE_NUM_MINORS; |
---|
1007 | 1030 | ace->gd->fops = &ace_fops; |
---|
| 1031 | + ace->gd->events = DISK_EVENT_MEDIA_CHANGE; |
---|
1008 | 1032 | ace->gd->queue = ace->queue; |
---|
1009 | 1033 | ace->gd->private_data = ace; |
---|
1010 | 1034 | snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a'); |
---|
.. | .. |
---|
1034 | 1058 | ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ); |
---|
1035 | 1059 | |
---|
1036 | 1060 | /* Now we can hook up the irq handler */ |
---|
1037 | | - if (ace->irq) { |
---|
| 1061 | + if (ace->irq > 0) { |
---|
1038 | 1062 | rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace); |
---|
1039 | 1063 | if (rc) { |
---|
1040 | 1064 | /* Failure - fall back to polled mode */ |
---|
1041 | 1065 | dev_err(ace->dev, "request_irq failed\n"); |
---|
1042 | | - ace->irq = 0; |
---|
| 1066 | + ace->irq = rc; |
---|
1043 | 1067 | } |
---|
1044 | 1068 | } |
---|
1045 | 1069 | |
---|
.. | .. |
---|
1055 | 1079 | (unsigned long long) ace->physaddr, ace->baseaddr, ace->irq); |
---|
1056 | 1080 | |
---|
1057 | 1081 | ace->media_change = 1; |
---|
1058 | | - ace_revalidate_disk(ace->gd); |
---|
| 1082 | + ace_media_changed(ace); |
---|
1059 | 1083 | |
---|
1060 | 1084 | /* Make the sysace device 'live' */ |
---|
1061 | 1085 | add_disk(ace->gd); |
---|
.. | .. |
---|
1068 | 1092 | put_disk(ace->gd); |
---|
1069 | 1093 | err_alloc_disk: |
---|
1070 | 1094 | blk_cleanup_queue(ace->queue); |
---|
| 1095 | + blk_mq_free_tag_set(&ace->tag_set); |
---|
1071 | 1096 | err_blk_initq: |
---|
1072 | 1097 | iounmap(ace->baseaddr); |
---|
1073 | 1098 | err_ioremap: |
---|
.. | .. |
---|
1083 | 1108 | put_disk(ace->gd); |
---|
1084 | 1109 | } |
---|
1085 | 1110 | |
---|
1086 | | - if (ace->queue) |
---|
| 1111 | + if (ace->queue) { |
---|
1087 | 1112 | blk_cleanup_queue(ace->queue); |
---|
| 1113 | + blk_mq_free_tag_set(&ace->tag_set); |
---|
| 1114 | + } |
---|
1088 | 1115 | |
---|
1089 | 1116 | tasklet_kill(&ace->fsm_tasklet); |
---|
1090 | 1117 | |
---|
1091 | | - if (ace->irq) |
---|
| 1118 | + if (ace->irq > 0) |
---|
1092 | 1119 | free_irq(ace->irq, ace); |
---|
1093 | 1120 | |
---|
1094 | 1121 | iounmap(ace->baseaddr); |
---|
.. | .. |
---|
1100 | 1127 | struct ace_device *ace; |
---|
1101 | 1128 | int rc; |
---|
1102 | 1129 | dev_dbg(dev, "ace_alloc(%p)\n", dev); |
---|
1103 | | - |
---|
1104 | | - if (!physaddr) { |
---|
1105 | | - rc = -ENODEV; |
---|
1106 | | - goto err_noreg; |
---|
1107 | | - } |
---|
1108 | 1130 | |
---|
1109 | 1131 | /* Allocate and initialize the ace device structure */ |
---|
1110 | 1132 | ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL); |
---|
.. | .. |
---|
1131 | 1153 | dev_set_drvdata(dev, NULL); |
---|
1132 | 1154 | kfree(ace); |
---|
1133 | 1155 | err_alloc: |
---|
1134 | | -err_noreg: |
---|
1135 | 1156 | dev_err(dev, "could not initialize device, err=%i\n", rc); |
---|
1136 | 1157 | return rc; |
---|
1137 | 1158 | } |
---|
.. | .. |
---|
1154 | 1175 | |
---|
1155 | 1176 | static int ace_probe(struct platform_device *dev) |
---|
1156 | 1177 | { |
---|
1157 | | - resource_size_t physaddr = 0; |
---|
1158 | 1178 | int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */ |
---|
| 1179 | + resource_size_t physaddr; |
---|
| 1180 | + struct resource *res; |
---|
1159 | 1181 | u32 id = dev->id; |
---|
1160 | | - int irq = 0; |
---|
| 1182 | + int irq; |
---|
1161 | 1183 | int i; |
---|
1162 | 1184 | |
---|
1163 | 1185 | dev_dbg(&dev->dev, "ace_probe(%p)\n", dev); |
---|
.. | .. |
---|
1168 | 1190 | if (of_find_property(dev->dev.of_node, "8-bit", NULL)) |
---|
1169 | 1191 | bus_width = ACE_BUS_WIDTH_8; |
---|
1170 | 1192 | |
---|
1171 | | - for (i = 0; i < dev->num_resources; i++) { |
---|
1172 | | - if (dev->resource[i].flags & IORESOURCE_MEM) |
---|
1173 | | - physaddr = dev->resource[i].start; |
---|
1174 | | - if (dev->resource[i].flags & IORESOURCE_IRQ) |
---|
1175 | | - irq = dev->resource[i].start; |
---|
1176 | | - } |
---|
| 1193 | + res = platform_get_resource(dev, IORESOURCE_MEM, 0); |
---|
| 1194 | + if (!res) |
---|
| 1195 | + return -EINVAL; |
---|
| 1196 | + |
---|
| 1197 | + physaddr = res->start; |
---|
| 1198 | + if (!physaddr) |
---|
| 1199 | + return -ENODEV; |
---|
| 1200 | + |
---|
| 1201 | + irq = platform_get_irq_optional(dev, 0); |
---|
1177 | 1202 | |
---|
1178 | 1203 | /* Call the bus-independent setup code */ |
---|
1179 | 1204 | return ace_alloc(&dev->dev, id, physaddr, irq, bus_width); |
---|