19f4a8a2dSBenjamin Herrenschmidt // SPDX-License-Identifier: GPL-2.0 29f4a8a2dSBenjamin Herrenschmidt /* 39f4a8a2dSBenjamin Herrenschmidt * Copyright (C) IBM Corporation 2017 49f4a8a2dSBenjamin Herrenschmidt * 59f4a8a2dSBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or modify 69f4a8a2dSBenjamin Herrenschmidt * it under the terms of the GNU General Public License version 2 as 79f4a8a2dSBenjamin Herrenschmidt * published by the Free Software Foundation. 89f4a8a2dSBenjamin Herrenschmidt * 99f4a8a2dSBenjamin Herrenschmidt * This program is distributed in the hope that it will be useful, 109f4a8a2dSBenjamin Herrenschmidt * but WITHOUT ANY WARRANTY; without even the implied warranty of 119f4a8a2dSBenjamin Herrenschmidt * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 129f4a8a2dSBenjamin Herrenschmidt * GNU General Public License for more details. 139f4a8a2dSBenjamin Herrenschmidt */ 149f4a8a2dSBenjamin Herrenschmidt 159f4a8a2dSBenjamin Herrenschmidt #include <linux/device.h> 169f4a8a2dSBenjamin Herrenschmidt #include <linux/errno.h> 179f4a8a2dSBenjamin Herrenschmidt #include <linux/fs.h> 189f4a8a2dSBenjamin Herrenschmidt #include <linux/fsi.h> 199f4a8a2dSBenjamin Herrenschmidt #include <linux/fsi-sbefifo.h> 209f4a8a2dSBenjamin Herrenschmidt #include <linux/kernel.h> 218b052dd6SBenjamin Herrenschmidt #include <linux/cdev.h> 229f4a8a2dSBenjamin Herrenschmidt #include <linux/module.h> 239f4a8a2dSBenjamin Herrenschmidt #include <linux/mutex.h> 249f4a8a2dSBenjamin Herrenschmidt #include <linux/of.h> 259f4a8a2dSBenjamin Herrenschmidt #include <linux/of_device.h> 269f4a8a2dSBenjamin Herrenschmidt #include <linux/of_platform.h> 279f4a8a2dSBenjamin Herrenschmidt #include <linux/sched.h> 289f4a8a2dSBenjamin Herrenschmidt #include <linux/slab.h> 299f4a8a2dSBenjamin Herrenschmidt #include <linux/uaccess.h> 309f4a8a2dSBenjamin Herrenschmidt #include <linux/delay.h> 319f4a8a2dSBenjamin Herrenschmidt #include <linux/uio.h> 329f4a8a2dSBenjamin Herrenschmidt #include <linux/vmalloc.h> 339f4a8a2dSBenjamin Herrenschmidt 349f4a8a2dSBenjamin Herrenschmidt /* 359f4a8a2dSBenjamin Herrenschmidt * The SBEFIFO is a pipe-like FSI device for communicating with 369f4a8a2dSBenjamin Herrenschmidt * the self boot engine on POWER processors. 379f4a8a2dSBenjamin Herrenschmidt */ 389f4a8a2dSBenjamin Herrenschmidt 399f4a8a2dSBenjamin Herrenschmidt #define DEVICE_NAME "sbefifo" 409f4a8a2dSBenjamin Herrenschmidt #define FSI_ENGID_SBE 0x22 419f4a8a2dSBenjamin Herrenschmidt 429f4a8a2dSBenjamin Herrenschmidt /* 439f4a8a2dSBenjamin Herrenschmidt * Register layout 449f4a8a2dSBenjamin Herrenschmidt */ 459f4a8a2dSBenjamin Herrenschmidt 469f4a8a2dSBenjamin Herrenschmidt /* Register banks */ 479f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_UP 0x00 /* FSI -> Host */ 489f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_DOWN 0x40 /* Host -> FSI */ 499f4a8a2dSBenjamin Herrenschmidt 509f4a8a2dSBenjamin Herrenschmidt /* Per-bank registers */ 519f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_FIFO 0x00 /* The FIFO itself */ 529f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS 0x04 /* Status register */ 539f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_PARITY_ERR 0x20000000 549f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_RESET_REQ 0x02000000 559f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_GOT_EOT 0x00800000 569f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_MAX_XFER_LIMIT 0x00400000 579f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_FULL 0x00200000 589f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_EMPTY 0x00100000 599f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_ECNT_MASK 0x000f0000 609f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_ECNT_SHIFT 16 619f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_VALID_MASK 0x0000ff00 629f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_VALID_SHIFT 8 639f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_EOT_MASK 0x000000ff 649f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_STS_EOT_SHIFT 0 659f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_EOT_RAISE 0x08 /* (Up only) Set End Of Transfer */ 669f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_REQ_RESET 0x0C /* (Up only) Reset Request */ 679f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_PERFORM_RESET 0x10 /* (Down only) Perform Reset */ 689f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_EOT_ACK 0x14 /* (Down only) Acknowledge EOT */ 699f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_DOWN_MAX 0x18 /* (Down only) Max transfer */ 709f4a8a2dSBenjamin Herrenschmidt 719f4a8a2dSBenjamin Herrenschmidt /* CFAM GP Mailbox SelfBoot Message register */ 729f4a8a2dSBenjamin Herrenschmidt #define CFAM_GP_MBOX_SBM_ADDR 0x2824 /* Converted 0x2809 */ 739f4a8a2dSBenjamin Herrenschmidt 749f4a8a2dSBenjamin Herrenschmidt #define CFAM_SBM_SBE_BOOTED 0x80000000 759f4a8a2dSBenjamin Herrenschmidt #define CFAM_SBM_SBE_ASYNC_FFDC 0x40000000 769f4a8a2dSBenjamin Herrenschmidt #define CFAM_SBM_SBE_STATE_MASK 0x00f00000 779f4a8a2dSBenjamin Herrenschmidt #define CFAM_SBM_SBE_STATE_SHIFT 20 789f4a8a2dSBenjamin Herrenschmidt 799f4a8a2dSBenjamin Herrenschmidt enum sbe_state 809f4a8a2dSBenjamin Herrenschmidt { 819f4a8a2dSBenjamin Herrenschmidt SBE_STATE_UNKNOWN = 0x0, // Unkown, initial state 829f4a8a2dSBenjamin Herrenschmidt SBE_STATE_IPLING = 0x1, // IPL'ing - autonomous mode (transient) 839f4a8a2dSBenjamin Herrenschmidt SBE_STATE_ISTEP = 0x2, // ISTEP - Running IPL by steps (transient) 849f4a8a2dSBenjamin Herrenschmidt SBE_STATE_MPIPL = 0x3, // MPIPL 859f4a8a2dSBenjamin Herrenschmidt SBE_STATE_RUNTIME = 0x4, // SBE Runtime 869f4a8a2dSBenjamin Herrenschmidt SBE_STATE_DMT = 0x5, // Dead Man Timer State (transient) 879f4a8a2dSBenjamin Herrenschmidt SBE_STATE_DUMP = 0x6, // Dumping 889f4a8a2dSBenjamin Herrenschmidt SBE_STATE_FAILURE = 0x7, // Internal SBE failure 899f4a8a2dSBenjamin Herrenschmidt SBE_STATE_QUIESCE = 0x8, // Final state - needs SBE reset to get out 909f4a8a2dSBenjamin Herrenschmidt }; 919f4a8a2dSBenjamin Herrenschmidt 929f4a8a2dSBenjamin Herrenschmidt /* FIFO depth */ 939f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_FIFO_DEPTH 8 949f4a8a2dSBenjamin Herrenschmidt 959f4a8a2dSBenjamin Herrenschmidt /* Helpers */ 969f4a8a2dSBenjamin Herrenschmidt #define sbefifo_empty(sts) ((sts) & SBEFIFO_STS_EMPTY) 979f4a8a2dSBenjamin Herrenschmidt #define sbefifo_full(sts) ((sts) & SBEFIFO_STS_FULL) 989f4a8a2dSBenjamin Herrenschmidt #define sbefifo_parity_err(sts) ((sts) & SBEFIFO_STS_PARITY_ERR) 999f4a8a2dSBenjamin Herrenschmidt #define sbefifo_populated(sts) (((sts) & SBEFIFO_STS_ECNT_MASK) >> SBEFIFO_STS_ECNT_SHIFT) 1009f4a8a2dSBenjamin Herrenschmidt #define sbefifo_vacant(sts) (SBEFIFO_FIFO_DEPTH - sbefifo_populated(sts)) 1019f4a8a2dSBenjamin Herrenschmidt #define sbefifo_eot_set(sts) (((sts) & SBEFIFO_STS_EOT_MASK) >> SBEFIFO_STS_EOT_SHIFT) 1029f4a8a2dSBenjamin Herrenschmidt 1039f4a8a2dSBenjamin Herrenschmidt /* Reset request timeout in ms */ 1049f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_RESET_TIMEOUT 10000 1059f4a8a2dSBenjamin Herrenschmidt 1069f4a8a2dSBenjamin Herrenschmidt /* Timeouts for commands in ms */ 1079f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_TIMEOUT_START_CMD 10000 1089f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_TIMEOUT_IN_CMD 1000 1099f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_TIMEOUT_START_RSP 10000 1109f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_TIMEOUT_IN_RSP 1000 1119f4a8a2dSBenjamin Herrenschmidt 1129f4a8a2dSBenjamin Herrenschmidt /* Other constants */ 1139f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_MAX_CMD_LEN PAGE_SIZE 1149f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_RESET_MAGIC 0x52534554 /* "RSET" */ 1159f4a8a2dSBenjamin Herrenschmidt 1169f4a8a2dSBenjamin Herrenschmidt struct sbefifo { 1179f4a8a2dSBenjamin Herrenschmidt uint32_t magic; 1189f4a8a2dSBenjamin Herrenschmidt #define SBEFIFO_MAGIC 0x53424546 /* "SBEF" */ 1199f4a8a2dSBenjamin Herrenschmidt struct fsi_device *fsi_dev; 1208b052dd6SBenjamin Herrenschmidt struct device dev; 1218b052dd6SBenjamin Herrenschmidt struct cdev cdev; 1229f4a8a2dSBenjamin Herrenschmidt struct mutex lock; 1239f4a8a2dSBenjamin Herrenschmidt bool broken; 1248b052dd6SBenjamin Herrenschmidt bool dead; 1259f4a8a2dSBenjamin Herrenschmidt bool async_ffdc; 1269f4a8a2dSBenjamin Herrenschmidt }; 1279f4a8a2dSBenjamin Herrenschmidt 1289f4a8a2dSBenjamin Herrenschmidt struct sbefifo_user { 1299f4a8a2dSBenjamin Herrenschmidt struct sbefifo *sbefifo; 1309f4a8a2dSBenjamin Herrenschmidt struct mutex file_lock; 1319f4a8a2dSBenjamin Herrenschmidt void *pending_cmd; 1329f4a8a2dSBenjamin Herrenschmidt size_t pending_len; 1339f4a8a2dSBenjamin Herrenschmidt }; 1349f4a8a2dSBenjamin Herrenschmidt 1359f4a8a2dSBenjamin Herrenschmidt static DEFINE_MUTEX(sbefifo_ffdc_mutex); 1369f4a8a2dSBenjamin Herrenschmidt 1378b052dd6SBenjamin Herrenschmidt 1384fcdc2d1SBenjamin Herrenschmidt static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, 1399f4a8a2dSBenjamin Herrenschmidt size_t ffdc_sz, bool internal) 1409f4a8a2dSBenjamin Herrenschmidt { 1419f4a8a2dSBenjamin Herrenschmidt int pack = 0; 1429f4a8a2dSBenjamin Herrenschmidt #define FFDC_LSIZE 60 1439f4a8a2dSBenjamin Herrenschmidt static char ffdc_line[FFDC_LSIZE]; 1449f4a8a2dSBenjamin Herrenschmidt char *p = ffdc_line; 1459f4a8a2dSBenjamin Herrenschmidt 1469f4a8a2dSBenjamin Herrenschmidt while (ffdc_sz) { 1479f4a8a2dSBenjamin Herrenschmidt u32 w0, w1, w2, i; 1489f4a8a2dSBenjamin Herrenschmidt if (ffdc_sz < 3) { 1499f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "SBE invalid FFDC package size %zd\n", ffdc_sz); 1509f4a8a2dSBenjamin Herrenschmidt return; 1519f4a8a2dSBenjamin Herrenschmidt } 1529f4a8a2dSBenjamin Herrenschmidt w0 = be32_to_cpu(*(ffdc++)); 1539f4a8a2dSBenjamin Herrenschmidt w1 = be32_to_cpu(*(ffdc++)); 1549f4a8a2dSBenjamin Herrenschmidt w2 = be32_to_cpu(*(ffdc++)); 1559f4a8a2dSBenjamin Herrenschmidt ffdc_sz -= 3; 1569f4a8a2dSBenjamin Herrenschmidt if ((w0 >> 16) != 0xFFDC) { 1579f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "SBE invalid FFDC package signature %08x %08x %08x\n", 1589f4a8a2dSBenjamin Herrenschmidt w0, w1, w2); 1599f4a8a2dSBenjamin Herrenschmidt break; 1609f4a8a2dSBenjamin Herrenschmidt } 1619f4a8a2dSBenjamin Herrenschmidt w0 &= 0xffff; 1629f4a8a2dSBenjamin Herrenschmidt if (w0 > ffdc_sz) { 1639f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "SBE FFDC package len %d words but only %zd remaining\n", 1649f4a8a2dSBenjamin Herrenschmidt w0, ffdc_sz); 1659f4a8a2dSBenjamin Herrenschmidt w0 = ffdc_sz; 1669f4a8a2dSBenjamin Herrenschmidt break; 1679f4a8a2dSBenjamin Herrenschmidt } 1689f4a8a2dSBenjamin Herrenschmidt if (internal) { 1699f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "+---- SBE FFDC package %d for async err -----+\n", 1709f4a8a2dSBenjamin Herrenschmidt pack++); 1719f4a8a2dSBenjamin Herrenschmidt } else { 1729f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "+---- SBE FFDC package %d for cmd %02x:%02x -----+\n", 1739f4a8a2dSBenjamin Herrenschmidt pack++, (w1 >> 8) & 0xff, w1 & 0xff); 1749f4a8a2dSBenjamin Herrenschmidt } 1759f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "| Response code: %08x |\n", w2); 1769f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "|-------------------------------------------|\n"); 1779f4a8a2dSBenjamin Herrenschmidt for (i = 0; i < w0; i++) { 1789f4a8a2dSBenjamin Herrenschmidt if ((i & 3) == 0) { 1799f4a8a2dSBenjamin Herrenschmidt p = ffdc_line; 1809f4a8a2dSBenjamin Herrenschmidt p += sprintf(p, "| %04x:", i << 4); 1819f4a8a2dSBenjamin Herrenschmidt } 1829f4a8a2dSBenjamin Herrenschmidt p += sprintf(p, " %08x", be32_to_cpu(*(ffdc++))); 1839f4a8a2dSBenjamin Herrenschmidt ffdc_sz--; 1849f4a8a2dSBenjamin Herrenschmidt if ((i & 3) == 3 || i == (w0 - 1)) { 1859f4a8a2dSBenjamin Herrenschmidt while ((i & 3) < 3) { 1869f4a8a2dSBenjamin Herrenschmidt p += sprintf(p, " "); 1879f4a8a2dSBenjamin Herrenschmidt i++; 1889f4a8a2dSBenjamin Herrenschmidt } 1899f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "%s |\n", ffdc_line); 1909f4a8a2dSBenjamin Herrenschmidt } 1919f4a8a2dSBenjamin Herrenschmidt } 1929f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "+-------------------------------------------+\n"); 1939f4a8a2dSBenjamin Herrenschmidt } 1944fcdc2d1SBenjamin Herrenschmidt } 1954fcdc2d1SBenjamin Herrenschmidt 1964fcdc2d1SBenjamin Herrenschmidt static void sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, 1974fcdc2d1SBenjamin Herrenschmidt size_t ffdc_sz, bool internal) 1984fcdc2d1SBenjamin Herrenschmidt { 1994fcdc2d1SBenjamin Herrenschmidt mutex_lock(&sbefifo_ffdc_mutex); 2004fcdc2d1SBenjamin Herrenschmidt __sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, internal); 201c00bac88SEddie James mutex_unlock(&sbefifo_ffdc_mutex); 2029f4a8a2dSBenjamin Herrenschmidt } 2039f4a8a2dSBenjamin Herrenschmidt 2049f4a8a2dSBenjamin Herrenschmidt int sbefifo_parse_status(struct device *dev, u16 cmd, __be32 *response, 2059f4a8a2dSBenjamin Herrenschmidt size_t resp_len, size_t *data_len) 2069f4a8a2dSBenjamin Herrenschmidt { 2079f4a8a2dSBenjamin Herrenschmidt u32 dh, s0, s1; 2089f4a8a2dSBenjamin Herrenschmidt size_t ffdc_sz; 2099f4a8a2dSBenjamin Herrenschmidt 2109f4a8a2dSBenjamin Herrenschmidt if (resp_len < 3) { 2119f4a8a2dSBenjamin Herrenschmidt pr_debug("sbefifo: cmd %04x, response too small: %zd\n", 2129f4a8a2dSBenjamin Herrenschmidt cmd, resp_len); 2139f4a8a2dSBenjamin Herrenschmidt return -ENXIO; 2149f4a8a2dSBenjamin Herrenschmidt } 2159f4a8a2dSBenjamin Herrenschmidt dh = be32_to_cpu(response[resp_len - 1]); 2169f4a8a2dSBenjamin Herrenschmidt if (dh > resp_len || dh < 3) { 2179f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "SBE cmd %02x:%02x status offset out of range: %d/%zd\n", 2189f4a8a2dSBenjamin Herrenschmidt cmd >> 8, cmd & 0xff, dh, resp_len); 2199f4a8a2dSBenjamin Herrenschmidt return -ENXIO; 2209f4a8a2dSBenjamin Herrenschmidt } 2219f4a8a2dSBenjamin Herrenschmidt s0 = be32_to_cpu(response[resp_len - dh]); 2229f4a8a2dSBenjamin Herrenschmidt s1 = be32_to_cpu(response[resp_len - dh + 1]); 2239f4a8a2dSBenjamin Herrenschmidt if (((s0 >> 16) != 0xC0DE) || ((s0 & 0xffff) != cmd)) { 2249f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "SBE cmd %02x:%02x, status signature invalid: 0x%08x 0x%08x\n", 2259f4a8a2dSBenjamin Herrenschmidt cmd >> 8, cmd & 0xff, s0, s1); 2269f4a8a2dSBenjamin Herrenschmidt return -ENXIO; 2279f4a8a2dSBenjamin Herrenschmidt } 2289f4a8a2dSBenjamin Herrenschmidt if (s1 != 0) { 2299f4a8a2dSBenjamin Herrenschmidt ffdc_sz = dh - 3; 2309f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "SBE error cmd %02x:%02x status=%04x:%04x\n", 2319f4a8a2dSBenjamin Herrenschmidt cmd >> 8, cmd & 0xff, s1 >> 16, s1 & 0xffff); 2329f4a8a2dSBenjamin Herrenschmidt if (ffdc_sz) 2339f4a8a2dSBenjamin Herrenschmidt sbefifo_dump_ffdc(dev, &response[resp_len - dh + 2], 2349f4a8a2dSBenjamin Herrenschmidt ffdc_sz, false); 2359f4a8a2dSBenjamin Herrenschmidt } 2369f4a8a2dSBenjamin Herrenschmidt if (data_len) 2379f4a8a2dSBenjamin Herrenschmidt *data_len = resp_len - dh; 2389f4a8a2dSBenjamin Herrenschmidt 2399f4a8a2dSBenjamin Herrenschmidt /* 2409f4a8a2dSBenjamin Herrenschmidt * Primary status don't have the top bit set, so can't be confused with 2419f4a8a2dSBenjamin Herrenschmidt * Linux negative error codes, so return the status word whole. 2429f4a8a2dSBenjamin Herrenschmidt */ 2439f4a8a2dSBenjamin Herrenschmidt return s1; 2449f4a8a2dSBenjamin Herrenschmidt } 2459f4a8a2dSBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(sbefifo_parse_status); 2469f4a8a2dSBenjamin Herrenschmidt 2479f4a8a2dSBenjamin Herrenschmidt static int sbefifo_regr(struct sbefifo *sbefifo, int reg, u32 *word) 2489f4a8a2dSBenjamin Herrenschmidt { 2499f4a8a2dSBenjamin Herrenschmidt __be32 raw_word; 2509f4a8a2dSBenjamin Herrenschmidt int rc; 2519f4a8a2dSBenjamin Herrenschmidt 2529f4a8a2dSBenjamin Herrenschmidt rc = fsi_device_read(sbefifo->fsi_dev, reg, &raw_word, 2539f4a8a2dSBenjamin Herrenschmidt sizeof(raw_word)); 2549f4a8a2dSBenjamin Herrenschmidt if (rc) 2559f4a8a2dSBenjamin Herrenschmidt return rc; 2569f4a8a2dSBenjamin Herrenschmidt 2579f4a8a2dSBenjamin Herrenschmidt *word = be32_to_cpu(raw_word); 2589f4a8a2dSBenjamin Herrenschmidt 2599f4a8a2dSBenjamin Herrenschmidt return 0; 2609f4a8a2dSBenjamin Herrenschmidt } 2619f4a8a2dSBenjamin Herrenschmidt 2629f4a8a2dSBenjamin Herrenschmidt static int sbefifo_regw(struct sbefifo *sbefifo, int reg, u32 word) 2639f4a8a2dSBenjamin Herrenschmidt { 2649f4a8a2dSBenjamin Herrenschmidt __be32 raw_word = cpu_to_be32(word); 2659f4a8a2dSBenjamin Herrenschmidt 2669f4a8a2dSBenjamin Herrenschmidt return fsi_device_write(sbefifo->fsi_dev, reg, &raw_word, 2679f4a8a2dSBenjamin Herrenschmidt sizeof(raw_word)); 2689f4a8a2dSBenjamin Herrenschmidt } 2699f4a8a2dSBenjamin Herrenschmidt 2709f4a8a2dSBenjamin Herrenschmidt static int sbefifo_check_sbe_state(struct sbefifo *sbefifo) 2719f4a8a2dSBenjamin Herrenschmidt { 2729f4a8a2dSBenjamin Herrenschmidt __be32 raw_word; 2739f4a8a2dSBenjamin Herrenschmidt u32 sbm; 2749f4a8a2dSBenjamin Herrenschmidt int rc; 2759f4a8a2dSBenjamin Herrenschmidt 2769f4a8a2dSBenjamin Herrenschmidt rc = fsi_slave_read(sbefifo->fsi_dev->slave, CFAM_GP_MBOX_SBM_ADDR, 2779f4a8a2dSBenjamin Herrenschmidt &raw_word, sizeof(raw_word)); 2789f4a8a2dSBenjamin Herrenschmidt if (rc) 2799f4a8a2dSBenjamin Herrenschmidt return rc; 2809f4a8a2dSBenjamin Herrenschmidt sbm = be32_to_cpu(raw_word); 2819f4a8a2dSBenjamin Herrenschmidt 2829f4a8a2dSBenjamin Herrenschmidt /* SBE booted at all ? */ 2839f4a8a2dSBenjamin Herrenschmidt if (!(sbm & CFAM_SBM_SBE_BOOTED)) 2849f4a8a2dSBenjamin Herrenschmidt return -ESHUTDOWN; 2859f4a8a2dSBenjamin Herrenschmidt 2869f4a8a2dSBenjamin Herrenschmidt /* Check its state */ 2879f4a8a2dSBenjamin Herrenschmidt switch ((sbm & CFAM_SBM_SBE_STATE_MASK) >> CFAM_SBM_SBE_STATE_SHIFT) { 2889f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_UNKNOWN: 2899f4a8a2dSBenjamin Herrenschmidt return -ESHUTDOWN; 2909f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_IPLING: 2919f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_ISTEP: 2929f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_MPIPL: 2939f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_DMT: 2949f4a8a2dSBenjamin Herrenschmidt return -EBUSY; 2959f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_RUNTIME: 2969f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_DUMP: /* Not sure about that one */ 2979f4a8a2dSBenjamin Herrenschmidt break; 2989f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_FAILURE: 2999f4a8a2dSBenjamin Herrenschmidt case SBE_STATE_QUIESCE: 3009f4a8a2dSBenjamin Herrenschmidt return -ESHUTDOWN; 3019f4a8a2dSBenjamin Herrenschmidt } 3029f4a8a2dSBenjamin Herrenschmidt 3039f4a8a2dSBenjamin Herrenschmidt /* Is there async FFDC available ? Remember it */ 3049f4a8a2dSBenjamin Herrenschmidt if (sbm & CFAM_SBM_SBE_ASYNC_FFDC) 3059f4a8a2dSBenjamin Herrenschmidt sbefifo->async_ffdc = true; 3069f4a8a2dSBenjamin Herrenschmidt 3079f4a8a2dSBenjamin Herrenschmidt return 0; 3089f4a8a2dSBenjamin Herrenschmidt } 3099f4a8a2dSBenjamin Herrenschmidt 3109f4a8a2dSBenjamin Herrenschmidt /* Don't flip endianness of data to/from FIFO, just pass through. */ 3119f4a8a2dSBenjamin Herrenschmidt static int sbefifo_down_read(struct sbefifo *sbefifo, __be32 *word) 3129f4a8a2dSBenjamin Herrenschmidt { 3139f4a8a2dSBenjamin Herrenschmidt return fsi_device_read(sbefifo->fsi_dev, SBEFIFO_DOWN, word, 3149f4a8a2dSBenjamin Herrenschmidt sizeof(*word)); 3159f4a8a2dSBenjamin Herrenschmidt } 3169f4a8a2dSBenjamin Herrenschmidt 3179f4a8a2dSBenjamin Herrenschmidt static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word) 3189f4a8a2dSBenjamin Herrenschmidt { 3199f4a8a2dSBenjamin Herrenschmidt return fsi_device_write(sbefifo->fsi_dev, SBEFIFO_UP, &word, 3209f4a8a2dSBenjamin Herrenschmidt sizeof(word)); 3219f4a8a2dSBenjamin Herrenschmidt } 3229f4a8a2dSBenjamin Herrenschmidt 3239f4a8a2dSBenjamin Herrenschmidt static int sbefifo_request_reset(struct sbefifo *sbefifo) 3249f4a8a2dSBenjamin Herrenschmidt { 3259f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 3269f4a8a2dSBenjamin Herrenschmidt u32 status, timeout; 3279f4a8a2dSBenjamin Herrenschmidt int rc; 3289f4a8a2dSBenjamin Herrenschmidt 3299f4a8a2dSBenjamin Herrenschmidt dev_dbg(dev, "Requesting FIFO reset\n"); 3309f4a8a2dSBenjamin Herrenschmidt 3319f4a8a2dSBenjamin Herrenschmidt /* Mark broken first, will be cleared if reset succeeds */ 3329f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 3339f4a8a2dSBenjamin Herrenschmidt 3349f4a8a2dSBenjamin Herrenschmidt /* Send reset request */ 3359f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_REQ_RESET, 1); 3369f4a8a2dSBenjamin Herrenschmidt if (rc) { 3379f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Sending reset request failed, rc=%d\n", rc); 3389f4a8a2dSBenjamin Herrenschmidt return rc; 3399f4a8a2dSBenjamin Herrenschmidt } 3409f4a8a2dSBenjamin Herrenschmidt 3419f4a8a2dSBenjamin Herrenschmidt /* Wait for it to complete */ 3429f4a8a2dSBenjamin Herrenschmidt for (timeout = 0; timeout < SBEFIFO_RESET_TIMEOUT; timeout++) { 3439f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &status); 3449f4a8a2dSBenjamin Herrenschmidt if (rc) { 3459f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Failed to read UP fifo status during reset" 3469f4a8a2dSBenjamin Herrenschmidt " , rc=%d\n", rc); 3479f4a8a2dSBenjamin Herrenschmidt return rc; 3489f4a8a2dSBenjamin Herrenschmidt } 3499f4a8a2dSBenjamin Herrenschmidt 3509f4a8a2dSBenjamin Herrenschmidt if (!(status & SBEFIFO_STS_RESET_REQ)) { 3519f4a8a2dSBenjamin Herrenschmidt dev_dbg(dev, "FIFO reset done\n"); 3529f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = false; 3539f4a8a2dSBenjamin Herrenschmidt return 0; 3549f4a8a2dSBenjamin Herrenschmidt } 3559f4a8a2dSBenjamin Herrenschmidt 3569f4a8a2dSBenjamin Herrenschmidt msleep(1); 3579f4a8a2dSBenjamin Herrenschmidt } 3589f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "FIFO reset timed out\n"); 3599f4a8a2dSBenjamin Herrenschmidt 3609f4a8a2dSBenjamin Herrenschmidt return -ETIMEDOUT; 3619f4a8a2dSBenjamin Herrenschmidt } 3629f4a8a2dSBenjamin Herrenschmidt 3639f4a8a2dSBenjamin Herrenschmidt static int sbefifo_cleanup_hw(struct sbefifo *sbefifo) 3649f4a8a2dSBenjamin Herrenschmidt { 3659f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 3669f4a8a2dSBenjamin Herrenschmidt u32 up_status, down_status; 3679f4a8a2dSBenjamin Herrenschmidt bool need_reset = false; 3689f4a8a2dSBenjamin Herrenschmidt int rc; 3699f4a8a2dSBenjamin Herrenschmidt 3709f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_check_sbe_state(sbefifo); 3719f4a8a2dSBenjamin Herrenschmidt if (rc) { 3729f4a8a2dSBenjamin Herrenschmidt dev_dbg(dev, "SBE state=%d\n", rc); 3739f4a8a2dSBenjamin Herrenschmidt return rc; 3749f4a8a2dSBenjamin Herrenschmidt } 3759f4a8a2dSBenjamin Herrenschmidt 3769f4a8a2dSBenjamin Herrenschmidt /* If broken, we don't need to look at status, go straight to reset */ 3779f4a8a2dSBenjamin Herrenschmidt if (sbefifo->broken) 3789f4a8a2dSBenjamin Herrenschmidt goto do_reset; 3799f4a8a2dSBenjamin Herrenschmidt 3809f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &up_status); 3819f4a8a2dSBenjamin Herrenschmidt if (rc) { 3829f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Cleanup: Reading UP status failed, rc=%d\n", rc); 3839f4a8a2dSBenjamin Herrenschmidt 3849f4a8a2dSBenjamin Herrenschmidt /* Will try reset again on next attempt at using it */ 3859f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 3869f4a8a2dSBenjamin Herrenschmidt return rc; 3879f4a8a2dSBenjamin Herrenschmidt } 3889f4a8a2dSBenjamin Herrenschmidt 3899f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regr(sbefifo, SBEFIFO_DOWN | SBEFIFO_STS, &down_status); 3909f4a8a2dSBenjamin Herrenschmidt if (rc) { 3919f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Cleanup: Reading DOWN status failed, rc=%d\n", rc); 3929f4a8a2dSBenjamin Herrenschmidt 3939f4a8a2dSBenjamin Herrenschmidt /* Will try reset again on next attempt at using it */ 3949f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 3959f4a8a2dSBenjamin Herrenschmidt return rc; 3969f4a8a2dSBenjamin Herrenschmidt } 3979f4a8a2dSBenjamin Herrenschmidt 3989f4a8a2dSBenjamin Herrenschmidt /* The FIFO already contains a reset request from the SBE ? */ 3999f4a8a2dSBenjamin Herrenschmidt if (down_status & SBEFIFO_STS_RESET_REQ) { 4009f4a8a2dSBenjamin Herrenschmidt dev_info(dev, "Cleanup: FIFO reset request set, resetting\n"); 4019f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regw(sbefifo, SBEFIFO_UP, SBEFIFO_PERFORM_RESET); 4029f4a8a2dSBenjamin Herrenschmidt if (rc) { 4039f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 4049f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n", rc); 4059f4a8a2dSBenjamin Herrenschmidt return rc; 4069f4a8a2dSBenjamin Herrenschmidt } 4079f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = false; 4089f4a8a2dSBenjamin Herrenschmidt return 0; 4099f4a8a2dSBenjamin Herrenschmidt } 4109f4a8a2dSBenjamin Herrenschmidt 4119f4a8a2dSBenjamin Herrenschmidt /* Parity error on either FIFO ? */ 4129f4a8a2dSBenjamin Herrenschmidt if ((up_status | down_status) & SBEFIFO_STS_PARITY_ERR) 4139f4a8a2dSBenjamin Herrenschmidt need_reset = true; 4149f4a8a2dSBenjamin Herrenschmidt 4159f4a8a2dSBenjamin Herrenschmidt /* Either FIFO not empty ? */ 4169f4a8a2dSBenjamin Herrenschmidt if (!((up_status & down_status) & SBEFIFO_STS_EMPTY)) 4179f4a8a2dSBenjamin Herrenschmidt need_reset = true; 4189f4a8a2dSBenjamin Herrenschmidt 4199f4a8a2dSBenjamin Herrenschmidt if (!need_reset) 4209f4a8a2dSBenjamin Herrenschmidt return 0; 4219f4a8a2dSBenjamin Herrenschmidt 4229f4a8a2dSBenjamin Herrenschmidt dev_info(dev, "Cleanup: FIFO not clean (up=0x%08x down=0x%08x)\n", 4239f4a8a2dSBenjamin Herrenschmidt up_status, down_status); 4249f4a8a2dSBenjamin Herrenschmidt 4259f4a8a2dSBenjamin Herrenschmidt do_reset: 4269f4a8a2dSBenjamin Herrenschmidt 4279f4a8a2dSBenjamin Herrenschmidt /* Mark broken, will be cleared if/when reset succeeds */ 4289f4a8a2dSBenjamin Herrenschmidt return sbefifo_request_reset(sbefifo); 4299f4a8a2dSBenjamin Herrenschmidt } 4309f4a8a2dSBenjamin Herrenschmidt 4319f4a8a2dSBenjamin Herrenschmidt static int sbefifo_wait(struct sbefifo *sbefifo, bool up, 4329f4a8a2dSBenjamin Herrenschmidt u32 *status, unsigned long timeout) 4339f4a8a2dSBenjamin Herrenschmidt { 4349f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 4359f4a8a2dSBenjamin Herrenschmidt unsigned long end_time; 4369f4a8a2dSBenjamin Herrenschmidt bool ready = false; 4379f4a8a2dSBenjamin Herrenschmidt u32 addr, sts = 0; 4389f4a8a2dSBenjamin Herrenschmidt int rc; 4399f4a8a2dSBenjamin Herrenschmidt 4409f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "Wait on %s fifo...\n", up ? "up" : "down"); 4419f4a8a2dSBenjamin Herrenschmidt 4429f4a8a2dSBenjamin Herrenschmidt addr = (up ? SBEFIFO_UP : SBEFIFO_DOWN) | SBEFIFO_STS; 4439f4a8a2dSBenjamin Herrenschmidt 4449f4a8a2dSBenjamin Herrenschmidt end_time = jiffies + timeout; 4459f4a8a2dSBenjamin Herrenschmidt while (!time_after(jiffies, end_time)) { 4469f4a8a2dSBenjamin Herrenschmidt cond_resched(); 4479f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regr(sbefifo, addr, &sts); 4489f4a8a2dSBenjamin Herrenschmidt if (rc < 0) { 4499f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "FSI error %d reading status register\n", rc); 4509f4a8a2dSBenjamin Herrenschmidt return rc; 4519f4a8a2dSBenjamin Herrenschmidt } 4529f4a8a2dSBenjamin Herrenschmidt if (!up && sbefifo_parity_err(sts)) { 4539f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Parity error in DOWN FIFO\n"); 4549f4a8a2dSBenjamin Herrenschmidt return -ENXIO; 4559f4a8a2dSBenjamin Herrenschmidt } 4569f4a8a2dSBenjamin Herrenschmidt ready = !(up ? sbefifo_full(sts) : sbefifo_empty(sts)); 4579f4a8a2dSBenjamin Herrenschmidt if (ready) 4589f4a8a2dSBenjamin Herrenschmidt break; 4599f4a8a2dSBenjamin Herrenschmidt } 4609f4a8a2dSBenjamin Herrenschmidt if (!ready) { 4619f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "%s FIFO Timeout ! status=%08x\n", up ? "UP" : "DOWN", sts); 4629f4a8a2dSBenjamin Herrenschmidt return -ETIMEDOUT; 4639f4a8a2dSBenjamin Herrenschmidt } 4649f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "End of wait status: %08x\n", sts); 4659f4a8a2dSBenjamin Herrenschmidt 4669f4a8a2dSBenjamin Herrenschmidt *status = sts; 4679f4a8a2dSBenjamin Herrenschmidt 4689f4a8a2dSBenjamin Herrenschmidt return 0; 4699f4a8a2dSBenjamin Herrenschmidt } 4709f4a8a2dSBenjamin Herrenschmidt 4719f4a8a2dSBenjamin Herrenschmidt static int sbefifo_send_command(struct sbefifo *sbefifo, 4729f4a8a2dSBenjamin Herrenschmidt const __be32 *command, size_t cmd_len) 4739f4a8a2dSBenjamin Herrenschmidt { 4749f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 4759f4a8a2dSBenjamin Herrenschmidt size_t len, chunk, vacant = 0, remaining = cmd_len; 4769f4a8a2dSBenjamin Herrenschmidt unsigned long timeout; 4779f4a8a2dSBenjamin Herrenschmidt u32 status; 4789f4a8a2dSBenjamin Herrenschmidt int rc; 4799f4a8a2dSBenjamin Herrenschmidt 4809f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "sending command (%zd words, cmd=%04x)\n", 4819f4a8a2dSBenjamin Herrenschmidt cmd_len, be32_to_cpu(command[1])); 4829f4a8a2dSBenjamin Herrenschmidt 4839f4a8a2dSBenjamin Herrenschmidt /* As long as there's something to send */ 4849f4a8a2dSBenjamin Herrenschmidt timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD); 4859f4a8a2dSBenjamin Herrenschmidt while (remaining) { 4869f4a8a2dSBenjamin Herrenschmidt /* Wait for room in the FIFO */ 4879f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_wait(sbefifo, true, &status, timeout); 4889f4a8a2dSBenjamin Herrenschmidt if (rc < 0) 4899f4a8a2dSBenjamin Herrenschmidt return rc; 4909f4a8a2dSBenjamin Herrenschmidt timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD); 4919f4a8a2dSBenjamin Herrenschmidt 4929f4a8a2dSBenjamin Herrenschmidt vacant = sbefifo_vacant(status); 4939f4a8a2dSBenjamin Herrenschmidt len = chunk = min(vacant, remaining); 4949f4a8a2dSBenjamin Herrenschmidt 4959f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, " status=%08x vacant=%zd chunk=%zd\n", 4969f4a8a2dSBenjamin Herrenschmidt status, vacant, chunk); 4979f4a8a2dSBenjamin Herrenschmidt 4989f4a8a2dSBenjamin Herrenschmidt /* Write as much as we can */ 4999f4a8a2dSBenjamin Herrenschmidt while (len--) { 5009f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_up_write(sbefifo, *(command++)); 5019f4a8a2dSBenjamin Herrenschmidt if (rc) { 5029f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "FSI error %d writing UP FIFO\n", rc); 5039f4a8a2dSBenjamin Herrenschmidt return rc; 5049f4a8a2dSBenjamin Herrenschmidt } 5059f4a8a2dSBenjamin Herrenschmidt } 5069f4a8a2dSBenjamin Herrenschmidt remaining -= chunk; 5079f4a8a2dSBenjamin Herrenschmidt vacant -= chunk; 5089f4a8a2dSBenjamin Herrenschmidt } 5099f4a8a2dSBenjamin Herrenschmidt 5109f4a8a2dSBenjamin Herrenschmidt /* If there's no room left, wait for some to write EOT */ 5119f4a8a2dSBenjamin Herrenschmidt if (!vacant) { 5129f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_wait(sbefifo, true, &status, timeout); 5139f4a8a2dSBenjamin Herrenschmidt if (rc) 5149f4a8a2dSBenjamin Herrenschmidt return rc; 5159f4a8a2dSBenjamin Herrenschmidt } 5169f4a8a2dSBenjamin Herrenschmidt 5179f4a8a2dSBenjamin Herrenschmidt /* Send an EOT */ 5189f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_EOT_RAISE, 0); 5199f4a8a2dSBenjamin Herrenschmidt if (rc) 5209f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "FSI error %d writing EOT\n", rc); 5219f4a8a2dSBenjamin Herrenschmidt return rc; 5229f4a8a2dSBenjamin Herrenschmidt } 5239f4a8a2dSBenjamin Herrenschmidt 5249f4a8a2dSBenjamin Herrenschmidt static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *response) 5259f4a8a2dSBenjamin Herrenschmidt { 5269f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 52729925138SJoel Stanley u32 status, eot_set; 5289f4a8a2dSBenjamin Herrenschmidt unsigned long timeout; 5299f4a8a2dSBenjamin Herrenschmidt bool overflow = false; 53029925138SJoel Stanley __be32 data; 5319f4a8a2dSBenjamin Herrenschmidt size_t len; 5329f4a8a2dSBenjamin Herrenschmidt int rc; 5339f4a8a2dSBenjamin Herrenschmidt 5349f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response)); 5359f4a8a2dSBenjamin Herrenschmidt 5369f4a8a2dSBenjamin Herrenschmidt timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_RSP); 5379f4a8a2dSBenjamin Herrenschmidt for (;;) { 5389f4a8a2dSBenjamin Herrenschmidt /* Grab FIFO status (this will handle parity errors) */ 5399f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_wait(sbefifo, false, &status, timeout); 5409f4a8a2dSBenjamin Herrenschmidt if (rc < 0) 5419f4a8a2dSBenjamin Herrenschmidt return rc; 5429f4a8a2dSBenjamin Herrenschmidt timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP); 5439f4a8a2dSBenjamin Herrenschmidt 5449f4a8a2dSBenjamin Herrenschmidt /* Decode status */ 5459f4a8a2dSBenjamin Herrenschmidt len = sbefifo_populated(status); 5469f4a8a2dSBenjamin Herrenschmidt eot_set = sbefifo_eot_set(status); 5479f4a8a2dSBenjamin Herrenschmidt 5489f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set); 5499f4a8a2dSBenjamin Herrenschmidt 5509f4a8a2dSBenjamin Herrenschmidt /* Go through the chunk */ 5519f4a8a2dSBenjamin Herrenschmidt while(len--) { 5529f4a8a2dSBenjamin Herrenschmidt /* Read the data */ 5539f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_down_read(sbefifo, &data); 5549f4a8a2dSBenjamin Herrenschmidt if (rc < 0) 5559f4a8a2dSBenjamin Herrenschmidt return rc; 5569f4a8a2dSBenjamin Herrenschmidt 5579f4a8a2dSBenjamin Herrenschmidt /* Was it an EOT ? */ 5589f4a8a2dSBenjamin Herrenschmidt if (eot_set & 0x80) { 5599f4a8a2dSBenjamin Herrenschmidt /* 5609f4a8a2dSBenjamin Herrenschmidt * There should be nothing else in the FIFO, 5619f4a8a2dSBenjamin Herrenschmidt * if there is, mark broken, this will force 5629f4a8a2dSBenjamin Herrenschmidt * a reset on next use, but don't fail the 5639f4a8a2dSBenjamin Herrenschmidt * command. 5649f4a8a2dSBenjamin Herrenschmidt */ 5659f4a8a2dSBenjamin Herrenschmidt if (len) { 5669f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "FIFO read hit" 5679f4a8a2dSBenjamin Herrenschmidt " EOT with still %zd data\n", 5689f4a8a2dSBenjamin Herrenschmidt len); 5699f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 5709f4a8a2dSBenjamin Herrenschmidt } 5719f4a8a2dSBenjamin Herrenschmidt 5729f4a8a2dSBenjamin Herrenschmidt /* We are done */ 5739f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_regw(sbefifo, 5749f4a8a2dSBenjamin Herrenschmidt SBEFIFO_DOWN | SBEFIFO_EOT_ACK, 0); 5759f4a8a2dSBenjamin Herrenschmidt 5769f4a8a2dSBenjamin Herrenschmidt /* 5779f4a8a2dSBenjamin Herrenschmidt * If that write fail, still complete the request but mark 5789f4a8a2dSBenjamin Herrenschmidt * the fifo as broken for subsequent reset (not much else 5799f4a8a2dSBenjamin Herrenschmidt * we can do here). 5809f4a8a2dSBenjamin Herrenschmidt */ 5819f4a8a2dSBenjamin Herrenschmidt if (rc) { 5829f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "FSI error %d ack'ing EOT\n", rc); 5839f4a8a2dSBenjamin Herrenschmidt sbefifo->broken = true; 5849f4a8a2dSBenjamin Herrenschmidt } 5859f4a8a2dSBenjamin Herrenschmidt 5869f4a8a2dSBenjamin Herrenschmidt /* Tell whether we overflowed */ 5879f4a8a2dSBenjamin Herrenschmidt return overflow ? -EOVERFLOW : 0; 5889f4a8a2dSBenjamin Herrenschmidt } 5899f4a8a2dSBenjamin Herrenschmidt 5909f4a8a2dSBenjamin Herrenschmidt /* Store it if there is room */ 5919f4a8a2dSBenjamin Herrenschmidt if (iov_iter_count(response) >= sizeof(__be32)) { 5929f4a8a2dSBenjamin Herrenschmidt if (copy_to_iter(&data, sizeof(__be32), response) < sizeof(__be32)) 5939f4a8a2dSBenjamin Herrenschmidt return -EFAULT; 5949f4a8a2dSBenjamin Herrenschmidt } else { 5959f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "Response overflowed !\n"); 5969f4a8a2dSBenjamin Herrenschmidt 5979f4a8a2dSBenjamin Herrenschmidt overflow = true; 5989f4a8a2dSBenjamin Herrenschmidt } 5999f4a8a2dSBenjamin Herrenschmidt 6009f4a8a2dSBenjamin Herrenschmidt /* Next EOT bit */ 6019f4a8a2dSBenjamin Herrenschmidt eot_set <<= 1; 6029f4a8a2dSBenjamin Herrenschmidt } 6039f4a8a2dSBenjamin Herrenschmidt } 6049f4a8a2dSBenjamin Herrenschmidt /* Shouldn't happen */ 6059f4a8a2dSBenjamin Herrenschmidt return -EIO; 6069f4a8a2dSBenjamin Herrenschmidt } 6079f4a8a2dSBenjamin Herrenschmidt 6089f4a8a2dSBenjamin Herrenschmidt static int sbefifo_do_command(struct sbefifo *sbefifo, 6099f4a8a2dSBenjamin Herrenschmidt const __be32 *command, size_t cmd_len, 6109f4a8a2dSBenjamin Herrenschmidt struct iov_iter *response) 6119f4a8a2dSBenjamin Herrenschmidt { 6129f4a8a2dSBenjamin Herrenschmidt /* Try sending the command */ 6139f4a8a2dSBenjamin Herrenschmidt int rc = sbefifo_send_command(sbefifo, command, cmd_len); 6149f4a8a2dSBenjamin Herrenschmidt if (rc) 6159f4a8a2dSBenjamin Herrenschmidt return rc; 6169f4a8a2dSBenjamin Herrenschmidt 6179f4a8a2dSBenjamin Herrenschmidt /* Now, get the response */ 6189f4a8a2dSBenjamin Herrenschmidt return sbefifo_read_response(sbefifo, response); 6199f4a8a2dSBenjamin Herrenschmidt } 6209f4a8a2dSBenjamin Herrenschmidt 6219f4a8a2dSBenjamin Herrenschmidt static void sbefifo_collect_async_ffdc(struct sbefifo *sbefifo) 6229f4a8a2dSBenjamin Herrenschmidt { 6239f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 6249f4a8a2dSBenjamin Herrenschmidt struct iov_iter ffdc_iter; 6259f4a8a2dSBenjamin Herrenschmidt struct kvec ffdc_iov; 6269f4a8a2dSBenjamin Herrenschmidt __be32 *ffdc; 6279f4a8a2dSBenjamin Herrenschmidt size_t ffdc_sz; 62829925138SJoel Stanley __be32 cmd[2]; 6299f4a8a2dSBenjamin Herrenschmidt int rc; 6309f4a8a2dSBenjamin Herrenschmidt 6319f4a8a2dSBenjamin Herrenschmidt sbefifo->async_ffdc = false; 6329f4a8a2dSBenjamin Herrenschmidt ffdc = vmalloc(SBEFIFO_MAX_FFDC_SIZE); 6339f4a8a2dSBenjamin Herrenschmidt if (!ffdc) { 6349f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Failed to allocate SBE FFDC buffer\n"); 6359f4a8a2dSBenjamin Herrenschmidt return; 6369f4a8a2dSBenjamin Herrenschmidt } 6379f4a8a2dSBenjamin Herrenschmidt ffdc_iov.iov_base = ffdc; 638a9ef97d0SBenjamin Herrenschmidt ffdc_iov.iov_len = SBEFIFO_MAX_FFDC_SIZE; 6399f4a8a2dSBenjamin Herrenschmidt iov_iter_kvec(&ffdc_iter, WRITE | ITER_KVEC, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE); 6409f4a8a2dSBenjamin Herrenschmidt cmd[0] = cpu_to_be32(2); 6419f4a8a2dSBenjamin Herrenschmidt cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_SBE_FFDC); 6429f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_do_command(sbefifo, cmd, 2, &ffdc_iter); 6439f4a8a2dSBenjamin Herrenschmidt if (rc != 0) { 6449f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Error %d retrieving SBE FFDC\n", rc); 6459f4a8a2dSBenjamin Herrenschmidt goto bail; 6469f4a8a2dSBenjamin Herrenschmidt } 6479f4a8a2dSBenjamin Herrenschmidt ffdc_sz = SBEFIFO_MAX_FFDC_SIZE - iov_iter_count(&ffdc_iter); 6489f4a8a2dSBenjamin Herrenschmidt ffdc_sz /= sizeof(__be32); 6499f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_parse_status(dev, SBEFIFO_CMD_GET_SBE_FFDC, ffdc, 6509f4a8a2dSBenjamin Herrenschmidt ffdc_sz, &ffdc_sz); 6519f4a8a2dSBenjamin Herrenschmidt if (rc != 0) { 6529f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Error %d decoding SBE FFDC\n", rc); 6539f4a8a2dSBenjamin Herrenschmidt goto bail; 6549f4a8a2dSBenjamin Herrenschmidt } 6559f4a8a2dSBenjamin Herrenschmidt if (ffdc_sz > 0) 6569f4a8a2dSBenjamin Herrenschmidt sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, true); 6579f4a8a2dSBenjamin Herrenschmidt bail: 6589f4a8a2dSBenjamin Herrenschmidt vfree(ffdc); 6599f4a8a2dSBenjamin Herrenschmidt 6609f4a8a2dSBenjamin Herrenschmidt } 6619f4a8a2dSBenjamin Herrenschmidt 6629f4a8a2dSBenjamin Herrenschmidt static int __sbefifo_submit(struct sbefifo *sbefifo, 6639f4a8a2dSBenjamin Herrenschmidt const __be32 *command, size_t cmd_len, 6649f4a8a2dSBenjamin Herrenschmidt struct iov_iter *response) 6659f4a8a2dSBenjamin Herrenschmidt { 6669f4a8a2dSBenjamin Herrenschmidt struct device *dev = &sbefifo->fsi_dev->dev; 6679f4a8a2dSBenjamin Herrenschmidt int rc; 6689f4a8a2dSBenjamin Herrenschmidt 6698b052dd6SBenjamin Herrenschmidt if (sbefifo->dead) 6708b052dd6SBenjamin Herrenschmidt return -ENODEV; 6718b052dd6SBenjamin Herrenschmidt 6729f4a8a2dSBenjamin Herrenschmidt if (cmd_len < 2 || be32_to_cpu(command[0]) != cmd_len) { 6739f4a8a2dSBenjamin Herrenschmidt dev_vdbg(dev, "Invalid command len %zd (header: %d)\n", 6749f4a8a2dSBenjamin Herrenschmidt cmd_len, be32_to_cpu(command[0])); 6759f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 6769f4a8a2dSBenjamin Herrenschmidt } 6779f4a8a2dSBenjamin Herrenschmidt 6789f4a8a2dSBenjamin Herrenschmidt /* First ensure the HW is in a clean state */ 6799f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_cleanup_hw(sbefifo); 6809f4a8a2dSBenjamin Herrenschmidt if (rc) 6819f4a8a2dSBenjamin Herrenschmidt return rc; 6829f4a8a2dSBenjamin Herrenschmidt 6839f4a8a2dSBenjamin Herrenschmidt /* Look for async FFDC first if any */ 6849f4a8a2dSBenjamin Herrenschmidt if (sbefifo->async_ffdc) 6859f4a8a2dSBenjamin Herrenschmidt sbefifo_collect_async_ffdc(sbefifo); 6869f4a8a2dSBenjamin Herrenschmidt 6879f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_do_command(sbefifo, command, cmd_len, response); 6889f4a8a2dSBenjamin Herrenschmidt if (rc != 0 && rc != -EOVERFLOW) 6899f4a8a2dSBenjamin Herrenschmidt goto fail; 6909f4a8a2dSBenjamin Herrenschmidt return rc; 6919f4a8a2dSBenjamin Herrenschmidt fail: 6929f4a8a2dSBenjamin Herrenschmidt /* 6939f4a8a2dSBenjamin Herrenschmidt * On failure, attempt a reset. Ignore the result, it will mark 6949f4a8a2dSBenjamin Herrenschmidt * the fifo broken if the reset fails 6959f4a8a2dSBenjamin Herrenschmidt */ 6969f4a8a2dSBenjamin Herrenschmidt sbefifo_request_reset(sbefifo); 6979f4a8a2dSBenjamin Herrenschmidt 6989f4a8a2dSBenjamin Herrenschmidt /* Return original error */ 6999f4a8a2dSBenjamin Herrenschmidt return rc; 7009f4a8a2dSBenjamin Herrenschmidt } 7019f4a8a2dSBenjamin Herrenschmidt 7029f4a8a2dSBenjamin Herrenschmidt /** 7039f4a8a2dSBenjamin Herrenschmidt * sbefifo_submit() - Submit and SBE fifo command and receive response 7049f4a8a2dSBenjamin Herrenschmidt * @dev: The sbefifo device 7059f4a8a2dSBenjamin Herrenschmidt * @command: The raw command data 7069f4a8a2dSBenjamin Herrenschmidt * @cmd_len: The command size (in 32-bit words) 7079f4a8a2dSBenjamin Herrenschmidt * @response: The output response buffer 7089f4a8a2dSBenjamin Herrenschmidt * @resp_len: In: Response buffer size, Out: Response size 7099f4a8a2dSBenjamin Herrenschmidt * 7109f4a8a2dSBenjamin Herrenschmidt * This will perform the entire operation. If the reponse buffer 7119f4a8a2dSBenjamin Herrenschmidt * overflows, returns -EOVERFLOW 7129f4a8a2dSBenjamin Herrenschmidt */ 7139f4a8a2dSBenjamin Herrenschmidt int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len, 7149f4a8a2dSBenjamin Herrenschmidt __be32 *response, size_t *resp_len) 7159f4a8a2dSBenjamin Herrenschmidt { 716d5c66e61SBenjamin Herrenschmidt struct sbefifo *sbefifo; 7179f4a8a2dSBenjamin Herrenschmidt struct iov_iter resp_iter; 7189f4a8a2dSBenjamin Herrenschmidt struct kvec resp_iov; 7199f4a8a2dSBenjamin Herrenschmidt size_t rbytes; 7209f4a8a2dSBenjamin Herrenschmidt int rc; 7219f4a8a2dSBenjamin Herrenschmidt 722d5c66e61SBenjamin Herrenschmidt if (!dev) 723d5c66e61SBenjamin Herrenschmidt return -ENODEV; 724d5c66e61SBenjamin Herrenschmidt sbefifo = dev_get_drvdata(dev); 725d5c66e61SBenjamin Herrenschmidt if (!sbefifo) 7269f4a8a2dSBenjamin Herrenschmidt return -ENODEV; 7279f4a8a2dSBenjamin Herrenschmidt if (WARN_ON_ONCE(sbefifo->magic != SBEFIFO_MAGIC)) 7289f4a8a2dSBenjamin Herrenschmidt return -ENODEV; 7299f4a8a2dSBenjamin Herrenschmidt if (!resp_len || !command || !response || cmd_len > SBEFIFO_MAX_CMD_LEN) 7309f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 7319f4a8a2dSBenjamin Herrenschmidt 7329f4a8a2dSBenjamin Herrenschmidt /* Prepare iov iterator */ 7339f4a8a2dSBenjamin Herrenschmidt rbytes = (*resp_len) * sizeof(__be32); 7349f4a8a2dSBenjamin Herrenschmidt resp_iov.iov_base = response; 7359f4a8a2dSBenjamin Herrenschmidt resp_iov.iov_len = rbytes; 7369f4a8a2dSBenjamin Herrenschmidt iov_iter_kvec(&resp_iter, WRITE | ITER_KVEC, &resp_iov, 1, rbytes); 7379f4a8a2dSBenjamin Herrenschmidt 7389f4a8a2dSBenjamin Herrenschmidt /* Perform the command */ 7399f4a8a2dSBenjamin Herrenschmidt mutex_lock(&sbefifo->lock); 7409f4a8a2dSBenjamin Herrenschmidt rc = __sbefifo_submit(sbefifo, command, cmd_len, &resp_iter); 7419f4a8a2dSBenjamin Herrenschmidt mutex_unlock(&sbefifo->lock); 7429f4a8a2dSBenjamin Herrenschmidt 7439f4a8a2dSBenjamin Herrenschmidt /* Extract the response length */ 7449f4a8a2dSBenjamin Herrenschmidt rbytes -= iov_iter_count(&resp_iter); 7459f4a8a2dSBenjamin Herrenschmidt *resp_len = rbytes / sizeof(__be32); 7469f4a8a2dSBenjamin Herrenschmidt 7479f4a8a2dSBenjamin Herrenschmidt return rc; 7489f4a8a2dSBenjamin Herrenschmidt } 7499f4a8a2dSBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(sbefifo_submit); 7509f4a8a2dSBenjamin Herrenschmidt 7519f4a8a2dSBenjamin Herrenschmidt /* 7529f4a8a2dSBenjamin Herrenschmidt * Char device interface 7539f4a8a2dSBenjamin Herrenschmidt */ 7549f4a8a2dSBenjamin Herrenschmidt static int sbefifo_user_open(struct inode *inode, struct file *file) 7559f4a8a2dSBenjamin Herrenschmidt { 7568b052dd6SBenjamin Herrenschmidt struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev); 7579f4a8a2dSBenjamin Herrenschmidt struct sbefifo_user *user; 7589f4a8a2dSBenjamin Herrenschmidt 7599f4a8a2dSBenjamin Herrenschmidt user = kzalloc(sizeof(struct sbefifo_user), GFP_KERNEL); 7609f4a8a2dSBenjamin Herrenschmidt if (!user) 7619f4a8a2dSBenjamin Herrenschmidt return -ENOMEM; 7629f4a8a2dSBenjamin Herrenschmidt 7639f4a8a2dSBenjamin Herrenschmidt file->private_data = user; 7649f4a8a2dSBenjamin Herrenschmidt user->sbefifo = sbefifo; 7659f4a8a2dSBenjamin Herrenschmidt user->pending_cmd = (void *)__get_free_page(GFP_KERNEL); 7669f4a8a2dSBenjamin Herrenschmidt if (!user->pending_cmd) { 7679f4a8a2dSBenjamin Herrenschmidt kfree(user); 7689f4a8a2dSBenjamin Herrenschmidt return -ENOMEM; 7699f4a8a2dSBenjamin Herrenschmidt } 7709f4a8a2dSBenjamin Herrenschmidt mutex_init(&user->file_lock); 7719f4a8a2dSBenjamin Herrenschmidt 7729f4a8a2dSBenjamin Herrenschmidt return 0; 7739f4a8a2dSBenjamin Herrenschmidt } 7749f4a8a2dSBenjamin Herrenschmidt 7759f4a8a2dSBenjamin Herrenschmidt static ssize_t sbefifo_user_read(struct file *file, char __user *buf, 7769f4a8a2dSBenjamin Herrenschmidt size_t len, loff_t *offset) 7779f4a8a2dSBenjamin Herrenschmidt { 7789f4a8a2dSBenjamin Herrenschmidt struct sbefifo_user *user = file->private_data; 7799f4a8a2dSBenjamin Herrenschmidt struct sbefifo *sbefifo; 7809f4a8a2dSBenjamin Herrenschmidt struct iov_iter resp_iter; 7819f4a8a2dSBenjamin Herrenschmidt struct iovec resp_iov; 7829f4a8a2dSBenjamin Herrenschmidt size_t cmd_len; 7839f4a8a2dSBenjamin Herrenschmidt int rc; 7849f4a8a2dSBenjamin Herrenschmidt 7859f4a8a2dSBenjamin Herrenschmidt if (!user) 7869f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 7879f4a8a2dSBenjamin Herrenschmidt sbefifo = user->sbefifo; 7889f4a8a2dSBenjamin Herrenschmidt if (len & 3) 7899f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 7909f4a8a2dSBenjamin Herrenschmidt 7919f4a8a2dSBenjamin Herrenschmidt mutex_lock(&user->file_lock); 7929f4a8a2dSBenjamin Herrenschmidt 7939f4a8a2dSBenjamin Herrenschmidt /* Cronus relies on -EAGAIN after a short read */ 7949f4a8a2dSBenjamin Herrenschmidt if (user->pending_len == 0) { 7959f4a8a2dSBenjamin Herrenschmidt rc = -EAGAIN; 7969f4a8a2dSBenjamin Herrenschmidt goto bail; 7979f4a8a2dSBenjamin Herrenschmidt } 7989f4a8a2dSBenjamin Herrenschmidt if (user->pending_len < 8) { 7999f4a8a2dSBenjamin Herrenschmidt rc = -EINVAL; 8009f4a8a2dSBenjamin Herrenschmidt goto bail; 8019f4a8a2dSBenjamin Herrenschmidt } 8029f4a8a2dSBenjamin Herrenschmidt cmd_len = user->pending_len >> 2; 8039f4a8a2dSBenjamin Herrenschmidt 8049f4a8a2dSBenjamin Herrenschmidt /* Prepare iov iterator */ 8059f4a8a2dSBenjamin Herrenschmidt resp_iov.iov_base = buf; 8069f4a8a2dSBenjamin Herrenschmidt resp_iov.iov_len = len; 8079f4a8a2dSBenjamin Herrenschmidt iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len); 8089f4a8a2dSBenjamin Herrenschmidt 8099f4a8a2dSBenjamin Herrenschmidt /* Perform the command */ 8109f4a8a2dSBenjamin Herrenschmidt mutex_lock(&sbefifo->lock); 8119f4a8a2dSBenjamin Herrenschmidt rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter); 8129f4a8a2dSBenjamin Herrenschmidt mutex_unlock(&sbefifo->lock); 8139f4a8a2dSBenjamin Herrenschmidt if (rc < 0) 8149f4a8a2dSBenjamin Herrenschmidt goto bail; 8159f4a8a2dSBenjamin Herrenschmidt 8169f4a8a2dSBenjamin Herrenschmidt /* Extract the response length */ 8179f4a8a2dSBenjamin Herrenschmidt rc = len - iov_iter_count(&resp_iter); 8189f4a8a2dSBenjamin Herrenschmidt bail: 8199f4a8a2dSBenjamin Herrenschmidt user->pending_len = 0; 8209f4a8a2dSBenjamin Herrenschmidt mutex_unlock(&user->file_lock); 8219f4a8a2dSBenjamin Herrenschmidt return rc; 8229f4a8a2dSBenjamin Herrenschmidt } 8239f4a8a2dSBenjamin Herrenschmidt 8249f4a8a2dSBenjamin Herrenschmidt static ssize_t sbefifo_user_write(struct file *file, const char __user *buf, 8259f4a8a2dSBenjamin Herrenschmidt size_t len, loff_t *offset) 8269f4a8a2dSBenjamin Herrenschmidt { 8279f4a8a2dSBenjamin Herrenschmidt struct sbefifo_user *user = file->private_data; 8289f4a8a2dSBenjamin Herrenschmidt struct sbefifo *sbefifo; 8299f4a8a2dSBenjamin Herrenschmidt int rc = len; 8309f4a8a2dSBenjamin Herrenschmidt 8319f4a8a2dSBenjamin Herrenschmidt if (!user) 8329f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 8339f4a8a2dSBenjamin Herrenschmidt sbefifo = user->sbefifo; 8349f4a8a2dSBenjamin Herrenschmidt if (len > SBEFIFO_MAX_CMD_LEN) 8359f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 8369f4a8a2dSBenjamin Herrenschmidt if (len & 3) 8379f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 8389f4a8a2dSBenjamin Herrenschmidt 8399f4a8a2dSBenjamin Herrenschmidt mutex_lock(&user->file_lock); 8409f4a8a2dSBenjamin Herrenschmidt 8419f4a8a2dSBenjamin Herrenschmidt /* Copy the command into the staging buffer */ 8429f4a8a2dSBenjamin Herrenschmidt if (copy_from_user(user->pending_cmd, buf, len)) { 8439f4a8a2dSBenjamin Herrenschmidt rc = -EFAULT; 8449f4a8a2dSBenjamin Herrenschmidt goto bail; 8459f4a8a2dSBenjamin Herrenschmidt } 8469f4a8a2dSBenjamin Herrenschmidt 8479f4a8a2dSBenjamin Herrenschmidt /* Check for the magic reset command */ 8489f4a8a2dSBenjamin Herrenschmidt if (len == 4 && be32_to_cpu(*(__be32 *)user->pending_cmd) == 8499f4a8a2dSBenjamin Herrenschmidt SBEFIFO_RESET_MAGIC) { 8509f4a8a2dSBenjamin Herrenschmidt 8519f4a8a2dSBenjamin Herrenschmidt /* Clear out any pending command */ 8529f4a8a2dSBenjamin Herrenschmidt user->pending_len = 0; 8539f4a8a2dSBenjamin Herrenschmidt 8549f4a8a2dSBenjamin Herrenschmidt /* Trigger reset request */ 8559f4a8a2dSBenjamin Herrenschmidt mutex_lock(&sbefifo->lock); 8569f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_request_reset(user->sbefifo); 8579f4a8a2dSBenjamin Herrenschmidt mutex_unlock(&sbefifo->lock); 8589f4a8a2dSBenjamin Herrenschmidt if (rc == 0) 8599f4a8a2dSBenjamin Herrenschmidt rc = 4; 8609f4a8a2dSBenjamin Herrenschmidt goto bail; 8619f4a8a2dSBenjamin Herrenschmidt } 8629f4a8a2dSBenjamin Herrenschmidt 8639f4a8a2dSBenjamin Herrenschmidt /* Update the staging buffer size */ 8649f4a8a2dSBenjamin Herrenschmidt user->pending_len = len; 8659f4a8a2dSBenjamin Herrenschmidt bail: 8669f4a8a2dSBenjamin Herrenschmidt mutex_unlock(&user->file_lock); 8679f4a8a2dSBenjamin Herrenschmidt 8689f4a8a2dSBenjamin Herrenschmidt /* And that's it, we'll issue the command on a read */ 8699f4a8a2dSBenjamin Herrenschmidt return rc; 8709f4a8a2dSBenjamin Herrenschmidt } 8719f4a8a2dSBenjamin Herrenschmidt 8729f4a8a2dSBenjamin Herrenschmidt static int sbefifo_user_release(struct inode *inode, struct file *file) 8739f4a8a2dSBenjamin Herrenschmidt { 8749f4a8a2dSBenjamin Herrenschmidt struct sbefifo_user *user = file->private_data; 8759f4a8a2dSBenjamin Herrenschmidt 8769f4a8a2dSBenjamin Herrenschmidt if (!user) 8779f4a8a2dSBenjamin Herrenschmidt return -EINVAL; 8789f4a8a2dSBenjamin Herrenschmidt 8799f4a8a2dSBenjamin Herrenschmidt free_page((unsigned long)user->pending_cmd); 8809f4a8a2dSBenjamin Herrenschmidt kfree(user); 8819f4a8a2dSBenjamin Herrenschmidt 8829f4a8a2dSBenjamin Herrenschmidt return 0; 8839f4a8a2dSBenjamin Herrenschmidt } 8849f4a8a2dSBenjamin Herrenschmidt 8859f4a8a2dSBenjamin Herrenschmidt static const struct file_operations sbefifo_fops = { 8869f4a8a2dSBenjamin Herrenschmidt .owner = THIS_MODULE, 8879f4a8a2dSBenjamin Herrenschmidt .open = sbefifo_user_open, 8889f4a8a2dSBenjamin Herrenschmidt .read = sbefifo_user_read, 8899f4a8a2dSBenjamin Herrenschmidt .write = sbefifo_user_write, 8909f4a8a2dSBenjamin Herrenschmidt .release = sbefifo_user_release, 8919f4a8a2dSBenjamin Herrenschmidt }; 8929f4a8a2dSBenjamin Herrenschmidt 8938b052dd6SBenjamin Herrenschmidt static void sbefifo_free(struct device *dev) 8948b052dd6SBenjamin Herrenschmidt { 8958b052dd6SBenjamin Herrenschmidt struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev); 8968b052dd6SBenjamin Herrenschmidt 8978b052dd6SBenjamin Herrenschmidt put_device(&sbefifo->fsi_dev->dev); 8988b052dd6SBenjamin Herrenschmidt kfree(sbefifo); 8998b052dd6SBenjamin Herrenschmidt } 9008b052dd6SBenjamin Herrenschmidt 9019f4a8a2dSBenjamin Herrenschmidt /* 9029f4a8a2dSBenjamin Herrenschmidt * Probe/remove 9039f4a8a2dSBenjamin Herrenschmidt */ 9049f4a8a2dSBenjamin Herrenschmidt 9059f4a8a2dSBenjamin Herrenschmidt static int sbefifo_probe(struct device *dev) 9069f4a8a2dSBenjamin Herrenschmidt { 9079f4a8a2dSBenjamin Herrenschmidt struct fsi_device *fsi_dev = to_fsi_dev(dev); 9089f4a8a2dSBenjamin Herrenschmidt struct sbefifo *sbefifo; 9099f4a8a2dSBenjamin Herrenschmidt struct device_node *np; 9109f4a8a2dSBenjamin Herrenschmidt struct platform_device *child; 9119f4a8a2dSBenjamin Herrenschmidt char child_name[32]; 9128b052dd6SBenjamin Herrenschmidt int rc, didx, child_idx = 0; 9139f4a8a2dSBenjamin Herrenschmidt 9149f4a8a2dSBenjamin Herrenschmidt dev_dbg(dev, "Found sbefifo device\n"); 9159f4a8a2dSBenjamin Herrenschmidt 9168b052dd6SBenjamin Herrenschmidt sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL); 9179f4a8a2dSBenjamin Herrenschmidt if (!sbefifo) 9189f4a8a2dSBenjamin Herrenschmidt return -ENOMEM; 9198b052dd6SBenjamin Herrenschmidt 9208b052dd6SBenjamin Herrenschmidt /* Grab a reference to the device (parent of our cdev), we'll drop it later */ 9218b052dd6SBenjamin Herrenschmidt if (!get_device(dev)) { 9228b052dd6SBenjamin Herrenschmidt kfree(sbefifo); 9238b052dd6SBenjamin Herrenschmidt return -ENODEV; 9248b052dd6SBenjamin Herrenschmidt } 9258b052dd6SBenjamin Herrenschmidt 9269f4a8a2dSBenjamin Herrenschmidt sbefifo->magic = SBEFIFO_MAGIC; 9279f4a8a2dSBenjamin Herrenschmidt sbefifo->fsi_dev = fsi_dev; 9288b052dd6SBenjamin Herrenschmidt dev_set_drvdata(dev, sbefifo); 9299f4a8a2dSBenjamin Herrenschmidt mutex_init(&sbefifo->lock); 9309f4a8a2dSBenjamin Herrenschmidt 9319f4a8a2dSBenjamin Herrenschmidt /* 9329f4a8a2dSBenjamin Herrenschmidt * Try cleaning up the FIFO. If this fails, we still register the 9339f4a8a2dSBenjamin Herrenschmidt * driver and will try cleaning things up again on the next access. 9349f4a8a2dSBenjamin Herrenschmidt */ 9359f4a8a2dSBenjamin Herrenschmidt rc = sbefifo_cleanup_hw(sbefifo); 9369f4a8a2dSBenjamin Herrenschmidt if (rc && rc != -ESHUTDOWN) 9379f4a8a2dSBenjamin Herrenschmidt dev_err(dev, "Initial HW cleanup failed, will retry later\n"); 9389f4a8a2dSBenjamin Herrenschmidt 9398b052dd6SBenjamin Herrenschmidt /* Create chardev for userspace access */ 9408b052dd6SBenjamin Herrenschmidt sbefifo->dev.type = &fsi_cdev_type; 9418b052dd6SBenjamin Herrenschmidt sbefifo->dev.parent = dev; 9428b052dd6SBenjamin Herrenschmidt sbefifo->dev.release = sbefifo_free; 9438b052dd6SBenjamin Herrenschmidt device_initialize(&sbefifo->dev); 9449f4a8a2dSBenjamin Herrenschmidt 9458b052dd6SBenjamin Herrenschmidt /* Allocate a minor in the FSI space */ 9468b052dd6SBenjamin Herrenschmidt rc = fsi_get_new_minor(fsi_dev, fsi_dev_sbefifo, &sbefifo->dev.devt, &didx); 9478b052dd6SBenjamin Herrenschmidt if (rc) 9488b052dd6SBenjamin Herrenschmidt goto err; 9499f4a8a2dSBenjamin Herrenschmidt 9508b052dd6SBenjamin Herrenschmidt dev_set_name(&sbefifo->dev, "sbefifo%d", didx); 9518b052dd6SBenjamin Herrenschmidt cdev_init(&sbefifo->cdev, &sbefifo_fops); 9528b052dd6SBenjamin Herrenschmidt rc = cdev_device_add(&sbefifo->cdev, &sbefifo->dev); 9539f4a8a2dSBenjamin Herrenschmidt if (rc) { 9548b052dd6SBenjamin Herrenschmidt dev_err(dev, "Error %d creating char device %s\n", 9558b052dd6SBenjamin Herrenschmidt rc, dev_name(&sbefifo->dev)); 9568b052dd6SBenjamin Herrenschmidt goto err_free_minor; 9579f4a8a2dSBenjamin Herrenschmidt } 9589f4a8a2dSBenjamin Herrenschmidt 9599f4a8a2dSBenjamin Herrenschmidt /* Create platform devs for dts child nodes (occ, etc) */ 9609f4a8a2dSBenjamin Herrenschmidt for_each_available_child_of_node(dev->of_node, np) { 9619f4a8a2dSBenjamin Herrenschmidt snprintf(child_name, sizeof(child_name), "%s-dev%d", 9628b052dd6SBenjamin Herrenschmidt dev_name(&sbefifo->dev), child_idx++); 9639f4a8a2dSBenjamin Herrenschmidt child = of_platform_device_create(np, child_name, dev); 9649f4a8a2dSBenjamin Herrenschmidt if (!child) 9659f4a8a2dSBenjamin Herrenschmidt dev_warn(dev, "failed to create child %s dev\n", 9669f4a8a2dSBenjamin Herrenschmidt child_name); 9679f4a8a2dSBenjamin Herrenschmidt } 9689f4a8a2dSBenjamin Herrenschmidt 9699f4a8a2dSBenjamin Herrenschmidt return 0; 9708b052dd6SBenjamin Herrenschmidt err_free_minor: 9718b052dd6SBenjamin Herrenschmidt fsi_free_minor(sbefifo->dev.devt); 9728b052dd6SBenjamin Herrenschmidt err: 9738b052dd6SBenjamin Herrenschmidt put_device(&sbefifo->dev); 9748b052dd6SBenjamin Herrenschmidt return rc; 9759f4a8a2dSBenjamin Herrenschmidt } 9769f4a8a2dSBenjamin Herrenschmidt 9779f4a8a2dSBenjamin Herrenschmidt static int sbefifo_unregister_child(struct device *dev, void *data) 9789f4a8a2dSBenjamin Herrenschmidt { 9799f4a8a2dSBenjamin Herrenschmidt struct platform_device *child = to_platform_device(dev); 9809f4a8a2dSBenjamin Herrenschmidt 9819f4a8a2dSBenjamin Herrenschmidt of_device_unregister(child); 9829f4a8a2dSBenjamin Herrenschmidt if (dev->of_node) 9839f4a8a2dSBenjamin Herrenschmidt of_node_clear_flag(dev->of_node, OF_POPULATED); 9849f4a8a2dSBenjamin Herrenschmidt 9859f4a8a2dSBenjamin Herrenschmidt return 0; 9869f4a8a2dSBenjamin Herrenschmidt } 9879f4a8a2dSBenjamin Herrenschmidt 9889f4a8a2dSBenjamin Herrenschmidt static int sbefifo_remove(struct device *dev) 9899f4a8a2dSBenjamin Herrenschmidt { 9909f4a8a2dSBenjamin Herrenschmidt struct sbefifo *sbefifo = dev_get_drvdata(dev); 9919f4a8a2dSBenjamin Herrenschmidt 9929f4a8a2dSBenjamin Herrenschmidt dev_dbg(dev, "Removing sbefifo device...\n"); 9939f4a8a2dSBenjamin Herrenschmidt 9948b052dd6SBenjamin Herrenschmidt mutex_lock(&sbefifo->lock); 9958b052dd6SBenjamin Herrenschmidt sbefifo->dead = true; 9968b052dd6SBenjamin Herrenschmidt mutex_unlock(&sbefifo->lock); 9979f4a8a2dSBenjamin Herrenschmidt 9988b052dd6SBenjamin Herrenschmidt cdev_device_del(&sbefifo->cdev, &sbefifo->dev); 9998b052dd6SBenjamin Herrenschmidt fsi_free_minor(sbefifo->dev.devt); 10008b052dd6SBenjamin Herrenschmidt device_for_each_child(dev, NULL, sbefifo_unregister_child); 10018b052dd6SBenjamin Herrenschmidt put_device(&sbefifo->dev); 10029f4a8a2dSBenjamin Herrenschmidt 10039f4a8a2dSBenjamin Herrenschmidt return 0; 10049f4a8a2dSBenjamin Herrenschmidt } 10059f4a8a2dSBenjamin Herrenschmidt 10069f4a8a2dSBenjamin Herrenschmidt static struct fsi_device_id sbefifo_ids[] = { 10079f4a8a2dSBenjamin Herrenschmidt { 10089f4a8a2dSBenjamin Herrenschmidt .engine_type = FSI_ENGID_SBE, 10099f4a8a2dSBenjamin Herrenschmidt .version = FSI_VERSION_ANY, 10109f4a8a2dSBenjamin Herrenschmidt }, 10119f4a8a2dSBenjamin Herrenschmidt { 0 } 10129f4a8a2dSBenjamin Herrenschmidt }; 10139f4a8a2dSBenjamin Herrenschmidt 10149f4a8a2dSBenjamin Herrenschmidt static struct fsi_driver sbefifo_drv = { 10159f4a8a2dSBenjamin Herrenschmidt .id_table = sbefifo_ids, 10169f4a8a2dSBenjamin Herrenschmidt .drv = { 10179f4a8a2dSBenjamin Herrenschmidt .name = DEVICE_NAME, 10189f4a8a2dSBenjamin Herrenschmidt .bus = &fsi_bus_type, 10199f4a8a2dSBenjamin Herrenschmidt .probe = sbefifo_probe, 10209f4a8a2dSBenjamin Herrenschmidt .remove = sbefifo_remove, 10219f4a8a2dSBenjamin Herrenschmidt } 10229f4a8a2dSBenjamin Herrenschmidt }; 10239f4a8a2dSBenjamin Herrenschmidt 10249f4a8a2dSBenjamin Herrenschmidt static int sbefifo_init(void) 10259f4a8a2dSBenjamin Herrenschmidt { 10269f4a8a2dSBenjamin Herrenschmidt return fsi_driver_register(&sbefifo_drv); 10279f4a8a2dSBenjamin Herrenschmidt } 10289f4a8a2dSBenjamin Herrenschmidt 10299f4a8a2dSBenjamin Herrenschmidt static void sbefifo_exit(void) 10309f4a8a2dSBenjamin Herrenschmidt { 10319f4a8a2dSBenjamin Herrenschmidt fsi_driver_unregister(&sbefifo_drv); 10329f4a8a2dSBenjamin Herrenschmidt } 10339f4a8a2dSBenjamin Herrenschmidt 10349f4a8a2dSBenjamin Herrenschmidt module_init(sbefifo_init); 10359f4a8a2dSBenjamin Herrenschmidt module_exit(sbefifo_exit); 10369f4a8a2dSBenjamin Herrenschmidt MODULE_LICENSE("GPL"); 10379f4a8a2dSBenjamin Herrenschmidt MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>"); 10389f4a8a2dSBenjamin Herrenschmidt MODULE_AUTHOR("Eddie James <eajames@linux.vnet.ibm.com>"); 10399f4a8a2dSBenjamin Herrenschmidt MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 10409f4a8a2dSBenjamin Herrenschmidt MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 10419f4a8a2dSBenjamin Herrenschmidt MODULE_DESCRIPTION("Linux device interface to the POWER Self Boot Engine"); 1042