1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2017, Joyent, Inc. 14 */ 15 16 #include <sys/scsi/adapters/smrt/smrt.h> 17 18 /* 19 * We must locate what the CISS specification describes as the "I2O 20 * registers". The Intelligent I/O (I2O) Architecture Specification describes 21 * this somewhat more coherently as "the memory region specified by the first 22 * base address configuration register indicating memory space (offset 10h, 23 * 14h, and so forth)". 24 */ 25 static int 26 smrt_locate_bar(pci_regspec_t *regs, unsigned nregs, 27 unsigned *i2o_bar) 28 { 29 /* 30 * Locate the first memory-mapped BAR: 31 */ 32 for (unsigned i = 0; i < nregs; i++) { 33 unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK; 34 35 if (type == PCI_ADDR_MEM32 || type == PCI_ADDR_MEM64) { 36 *i2o_bar = i; 37 return (DDI_SUCCESS); 38 } 39 } 40 41 return (DDI_FAILURE); 42 } 43 44 static int 45 smrt_locate_cfgtbl(smrt_t *smrt, pci_regspec_t *regs, unsigned nregs, 46 unsigned *ct_bar, uint32_t *baseaddr) 47 { 48 uint32_t cfg_offset, mem_offset; 49 unsigned want_type; 50 uint32_t want_bar; 51 52 cfg_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_CFG_OFFSET); 53 mem_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_MEM_OFFSET); 54 55 VERIFY3U(cfg_offset, !=, 0xffffffff); 56 VERIFY3U(mem_offset, !=, 0xffffffff); 57 58 /* 59 * Locate the Configuration Table. Three different values read 60 * from two I2O registers allow us to determine the location: 61 * - the correct PCI BAR offset is in the low 16 bits of 62 * CISS_I2O_CFGTBL_CFG_OFFSET 63 * - bit 16 is 0 for a 32-bit space, and 1 for 64-bit 64 * - the memory offset from the base of this BAR is 65 * in CISS_I2O_CFGTBL_MEM_OFFSET 66 */ 67 want_bar = (cfg_offset & 0xffff); 68 want_type = (cfg_offset & (1UL << 16)) ? PCI_ADDR_MEM64 : 69 PCI_ADDR_MEM32; 70 71 DTRACE_PROBE4(locate_cfgtbl, uint32_t, want_bar, unsigned, 72 want_type, uint32_t, cfg_offset, uint32_t, mem_offset); 73 74 for (unsigned i = 0; i < nregs; i++) { 75 unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK; 76 unsigned bar = PCI_REG_REG_G(regs[i].pci_phys_hi); 77 78 if (type != PCI_ADDR_MEM32 && type != PCI_ADDR_MEM64) { 79 continue; 80 } 81 82 if (bar == want_bar) { 83 *ct_bar = i; 84 *baseaddr = mem_offset; 85 return (DDI_SUCCESS); 86 } 87 } 88 89 return (DDI_FAILURE); 90 } 91 92 /* 93 * Determine the PCI vendor and device ID which is a proxy for which generation 94 * of controller we're working with. 95 */ 96 static int 97 smrt_identify_device(smrt_t *smrt) 98 { 99 ddi_acc_handle_t pci_hdl; 100 101 if (pci_config_setup(smrt->smrt_dip, &pci_hdl) != DDI_SUCCESS) 102 return (DDI_FAILURE); 103 104 smrt->smrt_pci_vendor = pci_config_get16(pci_hdl, PCI_CONF_VENID); 105 smrt->smrt_pci_device = pci_config_get16(pci_hdl, PCI_CONF_DEVID); 106 107 pci_config_teardown(&pci_hdl); 108 109 return (DDI_SUCCESS); 110 } 111 112 static int 113 smrt_map_device(smrt_t *smrt) 114 { 115 pci_regspec_t *regs; 116 uint_t regslen, nregs; 117 dev_info_t *dip = smrt->smrt_dip; 118 int r = DDI_FAILURE; 119 120 /* 121 * Get the list of PCI registers from the DDI property "regs": 122 */ 123 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 124 "reg", (int **)®s, ®slen) != DDI_PROP_SUCCESS) { 125 dev_err(dip, CE_WARN, "could not load \"reg\" DDI prop"); 126 return (DDI_FAILURE); 127 } 128 nregs = regslen * sizeof (int) / sizeof (pci_regspec_t); 129 130 if (smrt_locate_bar(regs, nregs, &smrt->smrt_i2o_bar) != 131 DDI_SUCCESS) { 132 dev_err(dip, CE_WARN, "did not find any memory BARs"); 133 goto out; 134 } 135 136 /* 137 * Map enough of the I2O memory space to enable us to talk to the 138 * device. 139 */ 140 if (ddi_regs_map_setup(dip, smrt->smrt_i2o_bar, &smrt->smrt_i2o_space, 141 CISS_I2O_MAP_BASE, CISS_I2O_MAP_LIMIT - CISS_I2O_MAP_BASE, 142 &smrt_dev_attributes, &smrt->smrt_i2o_handle) != DDI_SUCCESS) { 143 dev_err(dip, CE_WARN, "failed to map I2O registers"); 144 goto out; 145 } 146 smrt->smrt_init_level |= SMRT_INITLEVEL_I2O_MAPPED; 147 148 if (smrt_locate_cfgtbl(smrt, regs, nregs, &smrt->smrt_ct_bar, 149 &smrt->smrt_ct_baseaddr) != DDI_SUCCESS) { 150 dev_err(dip, CE_WARN, "could not find config table"); 151 goto out; 152 } 153 154 /* 155 * Map the Configuration Table. 156 */ 157 if (ddi_regs_map_setup(dip, smrt->smrt_ct_bar, 158 (caddr_t *)&smrt->smrt_ct, smrt->smrt_ct_baseaddr, 159 sizeof (CfgTable_t), &smrt_dev_attributes, 160 &smrt->smrt_ct_handle) != DDI_SUCCESS) { 161 dev_err(dip, CE_WARN, "could not map config table"); 162 goto out; 163 } 164 smrt->smrt_init_level |= SMRT_INITLEVEL_CFGTBL_MAPPED; 165 166 r = DDI_SUCCESS; 167 168 out: 169 ddi_prop_free(regs); 170 return (r); 171 } 172 173 int 174 smrt_device_setup(smrt_t *smrt) 175 { 176 /* 177 * Ensure that the controller is installed in such a fashion that it 178 * may become a DMA master. 179 */ 180 if (ddi_slaveonly(smrt->smrt_dip) == DDI_SUCCESS) { 181 dev_err(smrt->smrt_dip, CE_WARN, "device cannot become DMA " 182 "master"); 183 return (DDI_FAILURE); 184 } 185 186 if (smrt_identify_device(smrt) != DDI_SUCCESS) 187 goto fail; 188 189 if (smrt_map_device(smrt) != DDI_SUCCESS) { 190 goto fail; 191 } 192 193 return (DDI_SUCCESS); 194 195 fail: 196 smrt_device_teardown(smrt); 197 return (DDI_FAILURE); 198 } 199 200 void 201 smrt_device_teardown(smrt_t *smrt) 202 { 203 if (smrt->smrt_init_level & SMRT_INITLEVEL_CFGTBL_MAPPED) { 204 ddi_regs_map_free(&smrt->smrt_ct_handle); 205 smrt->smrt_init_level &= ~SMRT_INITLEVEL_CFGTBL_MAPPED; 206 } 207 208 if (smrt->smrt_init_level & SMRT_INITLEVEL_I2O_MAPPED) { 209 ddi_regs_map_free(&smrt->smrt_i2o_handle); 210 smrt->smrt_init_level &= ~SMRT_INITLEVEL_I2O_MAPPED; 211 } 212 } 213 214 uint32_t 215 smrt_get32(smrt_t *smrt, offset_t off) 216 { 217 VERIFY3S(off, >=, CISS_I2O_MAP_BASE); 218 VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT); 219 220 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 221 uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space + 222 (off - CISS_I2O_MAP_BASE)); 223 224 return (ddi_get32(smrt->smrt_i2o_handle, addr)); 225 } 226 227 void 228 smrt_put32(smrt_t *smrt, offset_t off, uint32_t val) 229 { 230 VERIFY3S(off, >=, CISS_I2O_MAP_BASE); 231 VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT); 232 233 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 234 uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space + 235 (off - CISS_I2O_MAP_BASE)); 236 237 ddi_put32(smrt->smrt_i2o_handle, addr, val); 238 } 239