1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PowerNV LPC bus handling. 4 * 5 * Copyright 2013 IBM Corp. 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/of.h> 10 #include <linux/bug.h> 11 #include <linux/gfp.h> 12 #include <linux/slab.h> 13 14 #include <asm/machdep.h> 15 #include <asm/firmware.h> 16 #include <asm/opal.h> 17 #include <asm/scom.h> 18 19 /* 20 * We could probably fit that inside the scom_map_t 21 * which is a void* after all but it's really too ugly 22 * so let's kmalloc it for now 23 */ 24 struct opal_scom_map { 25 uint32_t chip; 26 uint64_t addr; 27 }; 28 29 static scom_map_t opal_scom_map(struct device_node *dev, u64 reg, u64 count) 30 { 31 struct opal_scom_map *m; 32 const __be32 *gcid; 33 34 if (!of_get_property(dev, "scom-controller", NULL)) { 35 pr_err("%s: device %pOF is not a SCOM controller\n", 36 __func__, dev); 37 return SCOM_MAP_INVALID; 38 } 39 gcid = of_get_property(dev, "ibm,chip-id", NULL); 40 if (!gcid) { 41 pr_err("%s: device %pOF has no ibm,chip-id\n", 42 __func__, dev); 43 return SCOM_MAP_INVALID; 44 } 45 m = kmalloc(sizeof(*m), GFP_KERNEL); 46 if (!m) 47 return NULL; 48 m->chip = be32_to_cpup(gcid); 49 m->addr = reg; 50 51 return (scom_map_t)m; 52 } 53 54 static void opal_scom_unmap(scom_map_t map) 55 { 56 kfree(map); 57 } 58 59 static int opal_xscom_err_xlate(int64_t rc) 60 { 61 switch(rc) { 62 case 0: 63 return 0; 64 /* Add more translations if necessary */ 65 default: 66 return -EIO; 67 } 68 } 69 70 static u64 opal_scom_unmangle(u64 addr) 71 { 72 u64 tmp; 73 74 /* 75 * XSCOM addresses use the top nibble to set indirect mode and 76 * its form. Bits 4-11 are always 0. 77 * 78 * Because the debugfs interface uses signed offsets and shifts 79 * the address left by 3, we basically cannot use the top 4 bits 80 * of the 64-bit address, and thus cannot use the indirect bit. 81 * 82 * To deal with that, we support the indirect bits being in 83 * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we 84 * do the conversion here. 85 * 86 * For in-kernel use, we don't need to do this mangling. In 87 * kernel won't have bits 4-7 set. 88 * 89 * So: 90 * debugfs will always set 0-3 = 0 and clear 4-7 91 * kernel will always clear 0-3 = 0 and set 4-7 92 */ 93 tmp = addr; 94 tmp &= 0x0f00000000000000; 95 addr &= 0xf0ffffffffffffff; 96 addr |= tmp << 4; 97 98 return addr; 99 } 100 101 static int opal_scom_read(scom_map_t map, u64 reg, u64 *value) 102 { 103 struct opal_scom_map *m = map; 104 int64_t rc; 105 __be64 v; 106 107 reg = opal_scom_unmangle(m->addr + reg); 108 rc = opal_xscom_read(m->chip, reg, (__be64 *)__pa(&v)); 109 *value = be64_to_cpu(v); 110 return opal_xscom_err_xlate(rc); 111 } 112 113 static int opal_scom_write(scom_map_t map, u64 reg, u64 value) 114 { 115 struct opal_scom_map *m = map; 116 int64_t rc; 117 118 reg = opal_scom_unmangle(m->addr + reg); 119 rc = opal_xscom_write(m->chip, reg, value); 120 return opal_xscom_err_xlate(rc); 121 } 122 123 static const struct scom_controller opal_scom_controller = { 124 .map = opal_scom_map, 125 .unmap = opal_scom_unmap, 126 .read = opal_scom_read, 127 .write = opal_scom_write 128 }; 129 130 static int opal_xscom_init(void) 131 { 132 if (firmware_has_feature(FW_FEATURE_OPAL)) 133 scom_init(&opal_scom_controller); 134 return 0; 135 } 136 machine_arch_initcall(powernv, opal_xscom_init); 137