1 1.33 rin /* $NetBSD: ld_icp.c,v 1.33 2025/04/13 02:34:03 rin Exp $ */ 2 1.1 ad 3 1.1 ad /*- 4 1.1 ad * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 1.1 ad * All rights reserved. 6 1.1 ad * 7 1.1 ad * This code is derived from software contributed to The NetBSD Foundation 8 1.1 ad * by Andrew Doran. 9 1.1 ad * 10 1.1 ad * Redistribution and use in source and binary forms, with or without 11 1.1 ad * modification, are permitted provided that the following conditions 12 1.1 ad * are met: 13 1.1 ad * 1. Redistributions of source code must retain the above copyright 14 1.1 ad * notice, this list of conditions and the following disclaimer. 15 1.1 ad * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 ad * notice, this list of conditions and the following disclaimer in the 17 1.1 ad * documentation and/or other materials provided with the distribution. 18 1.1 ad * 19 1.1 ad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 ad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 ad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 ad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 ad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 ad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 ad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 ad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 ad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 ad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 ad * POSSIBILITY OF SUCH DAMAGE. 30 1.1 ad */ 31 1.1 ad 32 1.1 ad /* 33 1.1 ad * ICP-Vortex "GDT" front-end for ld(4) driver. 34 1.1 ad */ 35 1.1 ad 36 1.1 ad #include <sys/cdefs.h> 37 1.33 rin __KERNEL_RCSID(0, "$NetBSD: ld_icp.c,v 1.33 2025/04/13 02:34:03 rin Exp $"); 38 1.1 ad 39 1.1 ad #include <sys/param.h> 40 1.1 ad #include <sys/systm.h> 41 1.1 ad #include <sys/kernel.h> 42 1.1 ad #include <sys/device.h> 43 1.1 ad #include <sys/buf.h> 44 1.9 yamt #include <sys/bufq.h> 45 1.1 ad #include <sys/endian.h> 46 1.1 ad #include <sys/dkio.h> 47 1.1 ad #include <sys/disk.h> 48 1.29 pgoyette #include <sys/module.h> 49 1.18 ad #include <sys/bus.h> 50 1.1 ad 51 1.1 ad #include <dev/ldvar.h> 52 1.1 ad 53 1.1 ad #include <dev/ic/icpreg.h> 54 1.1 ad #include <dev/ic/icpvar.h> 55 1.1 ad 56 1.29 pgoyette #include "ioconf.h" 57 1.29 pgoyette 58 1.1 ad struct ld_icp_softc { 59 1.1 ad struct ld_softc sc_ld; 60 1.1 ad int sc_hwunit; 61 1.1 ad }; 62 1.1 ad 63 1.30 jdolecek static void ld_icp_attach(device_t, device_t, void *); 64 1.30 jdolecek static int ld_icp_detach(device_t, int); 65 1.30 jdolecek static int ld_icp_dobio(struct ld_icp_softc *, void *, int, int, int, 66 1.1 ad struct buf *); 67 1.33 rin static int ld_icp_dump(struct ld_softc *, void *, daddr_t, int); 68 1.31 jdolecek static int ld_icp_flush(struct ld_softc *, bool); 69 1.31 jdolecek static int ld_icp_ioctl(struct ld_softc *, u_long, void *, int32_t, bool); 70 1.30 jdolecek static void ld_icp_intr(struct icp_ccb *); 71 1.30 jdolecek static int ld_icp_match(device_t, cfdata_t, void *); 72 1.30 jdolecek static int ld_icp_start(struct ld_softc *, struct buf *); 73 1.1 ad 74 1.30 jdolecek static void ld_icp_adjqparam(device_t, int); 75 1.8 thorpej 76 1.22 tron CFATTACH_DECL_NEW(ld_icp, sizeof(struct ld_icp_softc), 77 1.8 thorpej ld_icp_match, ld_icp_attach, ld_icp_detach, NULL); 78 1.8 thorpej 79 1.8 thorpej static const struct icp_servicecb ld_icp_servicecb = { 80 1.8 thorpej ld_icp_adjqparam, 81 1.8 thorpej }; 82 1.1 ad 83 1.30 jdolecek static int 84 1.22 tron ld_icp_match(device_t parent, cfdata_t match, void *aux) 85 1.1 ad { 86 1.1 ad struct icp_attach_args *icpa; 87 1.1 ad 88 1.1 ad icpa = aux; 89 1.1 ad 90 1.1 ad return (icpa->icpa_unit < ICPA_UNIT_SCSI); 91 1.1 ad } 92 1.1 ad 93 1.30 jdolecek static void 94 1.22 tron ld_icp_attach(device_t parent, device_t self, void *aux) 95 1.1 ad { 96 1.22 tron struct icp_attach_args *icpa = aux; 97 1.22 tron struct ld_icp_softc *sc = device_private(self); 98 1.22 tron struct ld_softc *ld = &sc->sc_ld; 99 1.22 tron struct icp_softc *icp = device_private(parent); 100 1.22 tron struct icp_cachedrv *cd = &icp->icp_cdr[icpa->icpa_unit]; 101 1.1 ad struct icp_cdevinfo *cdi; 102 1.1 ad const char *str; 103 1.1 ad int t; 104 1.1 ad 105 1.22 tron ld->sc_dv = self; 106 1.1 ad 107 1.8 thorpej icp_register_servicecb(icp, icpa->icpa_unit, &ld_icp_servicecb); 108 1.8 thorpej 109 1.1 ad sc->sc_hwunit = icpa->icpa_unit; 110 1.1 ad ld->sc_maxxfer = ICP_MAX_XFER; 111 1.1 ad ld->sc_secsize = ICP_SECTOR_SIZE; 112 1.1 ad ld->sc_start = ld_icp_start; 113 1.1 ad ld->sc_dump = ld_icp_dump; 114 1.31 jdolecek ld->sc_ioctl = ld_icp_ioctl; 115 1.1 ad ld->sc_secperunit = cd->cd_size; 116 1.1 ad ld->sc_flags = LDF_ENABLED; 117 1.1 ad ld->sc_maxqueuecnt = icp->icp_openings; 118 1.1 ad 119 1.1 ad if (!icp_cmd(icp, ICP_CACHESERVICE, ICP_IOCTL, ICP_CACHE_DRV_INFO, 120 1.1 ad sc->sc_hwunit, sizeof(struct icp_cdevinfo))) { 121 1.10 briggs aprint_error(": unable to retrieve device info\n"); 122 1.1 ad ld->sc_flags = LDF_ENABLED; 123 1.1 ad goto out; 124 1.1 ad } 125 1.1 ad cdi = (struct icp_cdevinfo *)icp->icp_scr; 126 1.1 ad 127 1.10 briggs aprint_normal(": <%.8s>, ", cdi->ld_name); 128 1.1 ad t = le32toh(cdi->ld_dtype) >> 16; 129 1.1 ad 130 1.1 ad /* 131 1.1 ad * Print device type. 132 1.1 ad */ 133 1.1 ad if (le32toh(cdi->ld_dcnt) > 1 || le32toh(cdi->ld_slave) != -1) 134 1.1 ad str = "RAID-1"; 135 1.1 ad else if (t == 0) 136 1.1 ad str = "JBOD"; 137 1.1 ad else if (t == 1) 138 1.1 ad str = "RAID-0"; 139 1.1 ad else if (t == 2) 140 1.1 ad str = "Chain"; 141 1.1 ad else 142 1.1 ad str = "unknown type"; 143 1.1 ad 144 1.10 briggs aprint_normal("type: %s, ", str); 145 1.1 ad 146 1.1 ad /* 147 1.1 ad * Print device status. 148 1.1 ad */ 149 1.1 ad if (t > 2) 150 1.1 ad str = "missing"; 151 1.1 ad else if ((cdi->ld_error & 1) != 0) { 152 1.1 ad str = "fault"; 153 1.1 ad ld->sc_flags = LDF_ENABLED; 154 1.1 ad } else if ((cdi->ld_error & 2) != 0) 155 1.1 ad str = "invalid"; 156 1.1 ad else { 157 1.1 ad str = "optimal"; 158 1.1 ad ld->sc_flags = LDF_ENABLED; 159 1.1 ad } 160 1.11 perry 161 1.10 briggs aprint_normal("status: %s\n", str); 162 1.1 ad 163 1.1 ad out: 164 1.28 jdolecek ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); 165 1.1 ad } 166 1.1 ad 167 1.30 jdolecek static int 168 1.22 tron ld_icp_detach(device_t dv, int flags) 169 1.8 thorpej { 170 1.32 chs struct ld_softc *ldsc = device_private(dv); 171 1.8 thorpej int rv; 172 1.8 thorpej 173 1.32 chs if ((rv = ldbegindetach(ldsc, flags)) != 0) 174 1.8 thorpej return (rv); 175 1.32 chs ldenddetach(ldsc); 176 1.8 thorpej 177 1.8 thorpej return (0); 178 1.8 thorpej } 179 1.8 thorpej 180 1.30 jdolecek static int 181 1.1 ad ld_icp_dobio(struct ld_icp_softc *sc, void *data, int datasize, int blkno, 182 1.1 ad int dowrite, struct buf *bp) 183 1.1 ad { 184 1.1 ad struct icp_cachecmd *cc; 185 1.1 ad struct icp_ccb *ic; 186 1.1 ad struct icp_softc *icp; 187 1.1 ad int s, rv; 188 1.1 ad 189 1.22 tron icp = device_private(device_parent(sc->sc_ld.sc_dv)); 190 1.1 ad 191 1.1 ad /* 192 1.1 ad * Allocate a command control block. 193 1.1 ad */ 194 1.7 thorpej if (__predict_false((ic = icp_ccb_alloc(icp)) == NULL)) 195 1.6 thorpej return (EAGAIN); 196 1.1 ad 197 1.1 ad /* 198 1.1 ad * Map the data transfer. 199 1.1 ad */ 200 1.1 ad cc = &ic->ic_cmd.cmd_packet.cc; 201 1.1 ad ic->ic_sg = cc->cc_sg; 202 1.1 ad ic->ic_service = ICP_CACHESERVICE; 203 1.1 ad 204 1.1 ad rv = icp_ccb_map(icp, ic, data, datasize, 205 1.1 ad dowrite ? IC_XFER_OUT : IC_XFER_IN); 206 1.1 ad if (rv != 0) { 207 1.1 ad icp_ccb_free(icp, ic); 208 1.1 ad return (rv); 209 1.1 ad } 210 1.1 ad 211 1.1 ad /* 212 1.1 ad * Build the command. 213 1.1 ad */ 214 1.1 ad ic->ic_cmd.cmd_opcode = htole16((dowrite ? ICP_WRITE : ICP_READ)); 215 1.1 ad cc->cc_deviceno = htole16(sc->sc_hwunit); 216 1.1 ad cc->cc_blockno = htole32(blkno); 217 1.1 ad cc->cc_blockcnt = htole32(datasize / ICP_SECTOR_SIZE); 218 1.1 ad cc->cc_addr = ~0; /* scatter gather */ 219 1.1 ad cc->cc_nsgent = htole32(ic->ic_nsgent); 220 1.1 ad 221 1.1 ad ic->ic_cmdlen = (u_long)ic->ic_sg - (u_long)&ic->ic_cmd + 222 1.1 ad ic->ic_nsgent * sizeof(*ic->ic_sg); 223 1.1 ad 224 1.1 ad /* 225 1.1 ad * Fire it off to the controller. 226 1.1 ad */ 227 1.1 ad if (bp == NULL) { 228 1.1 ad s = splbio(); 229 1.1 ad rv = icp_ccb_poll(icp, ic, 10000); 230 1.1 ad icp_ccb_unmap(icp, ic); 231 1.1 ad icp_ccb_free(icp, ic); 232 1.1 ad splx(s); 233 1.1 ad } else { 234 1.1 ad ic->ic_intr = ld_icp_intr; 235 1.1 ad ic->ic_context = bp; 236 1.22 tron ic->ic_dv = sc->sc_ld.sc_dv; 237 1.1 ad icp_ccb_enqueue(icp, ic); 238 1.1 ad } 239 1.1 ad 240 1.1 ad return (rv); 241 1.1 ad } 242 1.1 ad 243 1.30 jdolecek static int 244 1.1 ad ld_icp_start(struct ld_softc *ld, struct buf *bp) 245 1.1 ad { 246 1.1 ad 247 1.1 ad return (ld_icp_dobio((struct ld_icp_softc *)ld, bp->b_data, 248 1.1 ad bp->b_bcount, bp->b_rawblkno, (bp->b_flags & B_READ) == 0, bp)); 249 1.1 ad } 250 1.1 ad 251 1.30 jdolecek static int 252 1.33 rin ld_icp_dump(struct ld_softc *ld, void *data, daddr_t blkno, int blkcnt) 253 1.1 ad { 254 1.1 ad 255 1.33 rin /* ld_icp_dobio() takes only an 'int' as a disk address */ 256 1.33 rin if (blkno + blkcnt - 1 > INT_MAX) 257 1.33 rin return (EIO); 258 1.33 rin 259 1.1 ad return (ld_icp_dobio((struct ld_icp_softc *)ld, data, 260 1.1 ad blkcnt * ld->sc_secsize, blkno, 1, NULL)); 261 1.1 ad } 262 1.1 ad 263 1.31 jdolecek /* ARGSUSED */ 264 1.30 jdolecek static int 265 1.31 jdolecek ld_icp_flush(struct ld_softc *ld, bool poll) 266 1.1 ad { 267 1.1 ad struct ld_icp_softc *sc; 268 1.1 ad struct icp_softc *icp; 269 1.1 ad struct icp_cachecmd *cc; 270 1.1 ad struct icp_ccb *ic; 271 1.1 ad int rv; 272 1.1 ad 273 1.1 ad sc = (struct ld_icp_softc *)ld; 274 1.22 tron icp = device_private(device_parent(ld->sc_dv)); 275 1.1 ad 276 1.6 thorpej ic = icp_ccb_alloc_wait(icp); 277 1.1 ad ic->ic_cmd.cmd_opcode = htole16(ICP_FLUSH); 278 1.1 ad 279 1.1 ad cc = &ic->ic_cmd.cmd_packet.cc; 280 1.1 ad cc->cc_deviceno = htole16(sc->sc_hwunit); 281 1.1 ad cc->cc_blockno = htole32(1); 282 1.1 ad cc->cc_blockcnt = 0; 283 1.1 ad cc->cc_addr = 0; 284 1.1 ad cc->cc_nsgent = 0; 285 1.1 ad 286 1.1 ad ic->ic_cmdlen = (u_long)&cc->cc_sg - (u_long)&ic->ic_cmd; 287 1.1 ad ic->ic_service = ICP_CACHESERVICE; 288 1.1 ad 289 1.1 ad rv = icp_ccb_wait(icp, ic, 30000); 290 1.1 ad icp_ccb_free(icp, ic); 291 1.1 ad 292 1.1 ad return (rv); 293 1.1 ad } 294 1.1 ad 295 1.31 jdolecek static int 296 1.31 jdolecek ld_icp_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll) 297 1.31 jdolecek { 298 1.31 jdolecek int error; 299 1.31 jdolecek 300 1.31 jdolecek switch (cmd) { 301 1.31 jdolecek case DIOCCACHESYNC: 302 1.31 jdolecek error = ld_icp_flush(ld, poll); 303 1.31 jdolecek break; 304 1.31 jdolecek 305 1.31 jdolecek default: 306 1.31 jdolecek error = EPASSTHROUGH; 307 1.31 jdolecek break; 308 1.31 jdolecek } 309 1.31 jdolecek 310 1.31 jdolecek return error; 311 1.31 jdolecek } 312 1.31 jdolecek 313 1.30 jdolecek static void 314 1.1 ad ld_icp_intr(struct icp_ccb *ic) 315 1.1 ad { 316 1.1 ad struct buf *bp; 317 1.1 ad struct ld_icp_softc *sc; 318 1.1 ad struct icp_softc *icp; 319 1.1 ad 320 1.1 ad bp = ic->ic_context; 321 1.26 chs sc = device_private(ic->ic_dv); 322 1.22 tron icp = device_private(device_parent(sc->sc_ld.sc_dv)); 323 1.1 ad 324 1.1 ad if (ic->ic_status != ICP_S_OK) { 325 1.19 cegger aprint_error_dev(ic->ic_dv, "request failed; status=0x%04x\n", 326 1.19 cegger ic->ic_status); 327 1.1 ad bp->b_error = EIO; 328 1.1 ad bp->b_resid = bp->b_bcount; 329 1.6 thorpej 330 1.6 thorpej icp->icp_evt.size = sizeof(icp->icp_evt.eu.sync); 331 1.26 chs icp->icp_evt.eu.sync.ionode = device_unit(icp->icp_dv); 332 1.6 thorpej icp->icp_evt.eu.sync.service = icp->icp_service; 333 1.6 thorpej icp->icp_evt.eu.sync.status = icp->icp_status; 334 1.6 thorpej icp->icp_evt.eu.sync.info = icp->icp_info; 335 1.6 thorpej icp->icp_evt.eu.sync.hostdrive = sc->sc_hwunit; 336 1.6 thorpej if (icp->icp_status >= 0x8000) 337 1.6 thorpej icp_store_event(icp, GDT_ES_SYNC, 0, &icp->icp_evt); 338 1.6 thorpej else 339 1.6 thorpej icp_store_event(icp, GDT_ES_SYNC, icp->icp_service, 340 1.6 thorpej &icp->icp_evt); 341 1.1 ad } else 342 1.1 ad bp->b_resid = 0; 343 1.1 ad 344 1.1 ad icp_ccb_unmap(icp, ic); 345 1.1 ad icp_ccb_free(icp, ic); 346 1.1 ad lddone(&sc->sc_ld, bp); 347 1.8 thorpej } 348 1.8 thorpej 349 1.30 jdolecek static void 350 1.22 tron ld_icp_adjqparam(device_t dv, int openings) 351 1.8 thorpej { 352 1.8 thorpej 353 1.32 chs ldadjqparam(device_private(dv), openings); 354 1.1 ad } 355 1.29 pgoyette 356 1.29 pgoyette MODULE(MODULE_CLASS_DRIVER, ld_icp, "ld"); /* no icp module yet */ 357 1.29 pgoyette 358 1.29 pgoyette #ifdef _MODULE 359 1.29 pgoyette /* 360 1.29 pgoyette * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_cd" 361 1.29 pgoyette * XXX it will be defined in the common-code module 362 1.29 pgoyette */ 363 1.29 pgoyette #undef CFDRIVER_DECL 364 1.29 pgoyette #define CFDRIVER_DECL(name, class, attr) 365 1.29 pgoyette #include "ioconf.c" 366 1.29 pgoyette #endif 367 1.29 pgoyette 368 1.29 pgoyette static int 369 1.29 pgoyette ld_icp_modcmd(modcmd_t cmd, void *opaque) 370 1.29 pgoyette { 371 1.29 pgoyette #ifdef _MODULE 372 1.29 pgoyette /* 373 1.29 pgoyette * We ignore the cfdriver_vec[] that ioconf provides, since 374 1.29 pgoyette * the cfdrivers are attached already. 375 1.29 pgoyette */ 376 1.29 pgoyette static struct cfdriver * const no_cfdriver_vec[] = { NULL }; 377 1.29 pgoyette #endif 378 1.29 pgoyette int error = 0; 379 1.29 pgoyette 380 1.29 pgoyette #ifdef _MODULE 381 1.29 pgoyette switch (cmd) { 382 1.29 pgoyette case MODULE_CMD_INIT: 383 1.29 pgoyette error = config_init_component(no_cfdriver_vec, 384 1.29 pgoyette cfattach_ioconf_ld_icp, cfdata_ioconf_ld_icp); 385 1.29 pgoyette break; 386 1.29 pgoyette case MODULE_CMD_FINI: 387 1.29 pgoyette error = config_fini_component(no_cfdriver_vec, 388 1.29 pgoyette cfattach_ioconf_ld_icp, cfdata_ioconf_ld_icp); 389 1.29 pgoyette break; 390 1.29 pgoyette default: 391 1.29 pgoyette error = ENOTTY; 392 1.29 pgoyette break; 393 1.29 pgoyette } 394 1.29 pgoyette #endif 395 1.29 pgoyette 396 1.29 pgoyette return error; 397 1.29 pgoyette } 398