0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define KMSG_COMPONENT "hmcdrv"
0011 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0012
0013 #include <linux/kernel.h>
0014 #include <linux/mm.h>
0015 #include <linux/irq.h>
0016 #include <linux/wait.h>
0017 #include <linux/string.h>
0018 #include <asm/asm-extable.h>
0019 #include <asm/ctl_reg.h>
0020 #include <asm/diag.h>
0021
0022 #include "hmcdrv_ftp.h"
0023 #include "diag_ftp.h"
0024
0025
0026 #define DIAG_FTP_RET_OK 0
0027 #define DIAG_FTP_RET_EBUSY 4
0028 #define DIAG_FTP_RET_EIO 8
0029
0030 #define DIAG_FTP_RET_EPERM 2
0031
0032
0033 #define DIAG_FTP_STAT_OK 0U
0034 #define DIAG_FTP_STAT_PGCC 4U
0035 #define DIAG_FTP_STAT_PGIOE 8U
0036 #define DIAG_FTP_STAT_TIMEOUT 12U
0037 #define DIAG_FTP_STAT_EBASE 16U
0038 #define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U)
0039 #define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U)
0040 #define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U)
0041 #define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U)
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 struct diag_ftp_ldfpl {
0056 u64 bufaddr;
0057 u64 buflen;
0058 u64 offset;
0059 u64 intparm;
0060 u64 transferred;
0061 u64 fsize;
0062 u64 failaddr;
0063 u64 spare;
0064 u8 fident[HMCDRV_FTP_FIDENT_MAX];
0065 } __packed;
0066
0067 static DECLARE_COMPLETION(diag_ftp_rx_complete);
0068 static int diag_ftp_subcode;
0069
0070
0071
0072
0073
0074
0075
0076 static void diag_ftp_handler(struct ext_code extirq,
0077 unsigned int param32,
0078 unsigned long param64)
0079 {
0080 if ((extirq.subcode >> 8) != 8)
0081 return;
0082
0083 inc_irq_stat(IRQEXT_FTP);
0084 diag_ftp_subcode = extirq.subcode & 0xffU;
0085 complete(&diag_ftp_rx_complete);
0086 }
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103 static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
0104 enum hmcdrv_ftp_cmdid cmd)
0105 {
0106 int rc;
0107
0108 diag_stat_inc(DIAG_STAT_X2C4);
0109 asm volatile(
0110 " diag %[addr],%[cmd],0x2c4\n"
0111 "0: j 2f\n"
0112 "1: la %[rc],%[err]\n"
0113 "2:\n"
0114 EX_TABLE(0b, 1b)
0115 : [rc] "=d" (rc), "+m" (*fpl)
0116 : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
0117 [err] "i" (DIAG_FTP_RET_EPERM)
0118 : "cc");
0119
0120 switch (rc) {
0121 case DIAG_FTP_RET_OK:
0122 return 0;
0123 case DIAG_FTP_RET_EBUSY:
0124 return -EBUSY;
0125 case DIAG_FTP_RET_EPERM:
0126 return -EPERM;
0127 case DIAG_FTP_RET_EIO:
0128 default:
0129 return -EIO;
0130 }
0131 }
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143 ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
0144 {
0145 struct diag_ftp_ldfpl *ldfpl;
0146 ssize_t len;
0147 #ifdef DEBUG
0148 unsigned long start_jiffies;
0149
0150 pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n",
0151 ftp->fname, ftp->len);
0152 start_jiffies = jiffies;
0153 #endif
0154 init_completion(&diag_ftp_rx_complete);
0155
0156 ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0157 if (!ldfpl) {
0158 len = -ENOMEM;
0159 goto out;
0160 }
0161
0162 len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident));
0163 if (len >= HMCDRV_FTP_FIDENT_MAX) {
0164 len = -EINVAL;
0165 goto out_free;
0166 }
0167
0168 ldfpl->transferred = 0;
0169 ldfpl->fsize = 0;
0170 ldfpl->offset = ftp->ofs;
0171 ldfpl->buflen = ftp->len;
0172 ldfpl->bufaddr = virt_to_phys(ftp->buf);
0173
0174 len = diag_ftp_2c4(ldfpl, ftp->id);
0175 if (len)
0176 goto out_free;
0177
0178
0179
0180
0181
0182 wait_for_completion(&diag_ftp_rx_complete);
0183
0184 #ifdef DEBUG
0185 pr_debug("completed DIAG X'2C4' after %lu ms\n",
0186 (jiffies - start_jiffies) * 1000 / HZ);
0187 pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n",
0188 diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize);
0189 #endif
0190
0191 switch (diag_ftp_subcode) {
0192 case DIAG_FTP_STAT_OK:
0193 len = ldfpl->transferred;
0194 if (fsize)
0195 *fsize = ldfpl->fsize;
0196 break;
0197 case DIAG_FTP_STAT_LDNPERM:
0198 len = -EPERM;
0199 break;
0200 case DIAG_FTP_STAT_LDRUNS:
0201 len = -EBUSY;
0202 break;
0203 case DIAG_FTP_STAT_LDFAIL:
0204 len = -ENOENT;
0205 break;
0206 default:
0207 len = -EIO;
0208 break;
0209 }
0210
0211 out_free:
0212 free_page((unsigned long) ldfpl);
0213 out:
0214 return len;
0215 }
0216
0217
0218
0219
0220
0221
0222 int diag_ftp_startup(void)
0223 {
0224 int rc;
0225
0226 rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
0227 if (rc)
0228 return rc;
0229
0230 irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
0231 return 0;
0232 }
0233
0234
0235
0236
0237 void diag_ftp_shutdown(void)
0238 {
0239 irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
0240 unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
0241 }