| .. | .. |
|---|
| 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); |
|---|