538 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 4 * of PCI-SCSI IO processors.5 *6 * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>7 *8 * This driver is derived from the Linux sym53c8xx driver.9 * Copyright (C) 1998-2000 Gerard Roudier10 *11 * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 12 * a port of the FreeBSD ncr driver to Linux-1.2.13.13 *14 * The original ncr driver has been written for 386bsd and FreeBSD by15 * Wolfgang Stanglmeier <wolf@cologne.de>16 * Stefan Esser <se@mi.Uni-Koeln.de>17 * Copyright (C) 1994 Wolfgang Stanglmeier18 *19 * Other major contributions:20 *21 * NVRAM detection and reading.22 * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>23 *24 *-----------------------------------------------------------------------------25 */26 27#include "sym_glue.h"28 29/*30 * Macros used for all firmwares.31 */32#define SYM_GEN_A(s, label) ((short) offsetof(s, label)),33#define SYM_GEN_B(s, label) ((short) offsetof(s, label)),34#define SYM_GEN_Z(s, label) ((short) offsetof(s, label)),35#define PADDR_A(label) SYM_GEN_PADDR_A(struct SYM_FWA_SCR, label)36#define PADDR_B(label) SYM_GEN_PADDR_B(struct SYM_FWB_SCR, label)37 38 39#if SYM_CONF_GENERIC_SUPPORT40/*41 * Allocate firmware #1 script area.42 */43#define SYM_FWA_SCR sym_fw1a_scr44#define SYM_FWB_SCR sym_fw1b_scr45#define SYM_FWZ_SCR sym_fw1z_scr46#include "sym_fw1.h"47static struct sym_fwa_ofs sym_fw1a_ofs = {48 SYM_GEN_FW_A(struct SYM_FWA_SCR)49};50static struct sym_fwb_ofs sym_fw1b_ofs = {51 SYM_GEN_FW_B(struct SYM_FWB_SCR)52};53static struct sym_fwz_ofs sym_fw1z_ofs = {54 SYM_GEN_FW_Z(struct SYM_FWZ_SCR)55};56#undef SYM_FWA_SCR57#undef SYM_FWB_SCR58#undef SYM_FWZ_SCR59#endif /* SYM_CONF_GENERIC_SUPPORT */60 61/*62 * Allocate firmware #2 script area.63 */64#define SYM_FWA_SCR sym_fw2a_scr65#define SYM_FWB_SCR sym_fw2b_scr66#define SYM_FWZ_SCR sym_fw2z_scr67#include "sym_fw2.h"68static struct sym_fwa_ofs sym_fw2a_ofs = {69 SYM_GEN_FW_A(struct SYM_FWA_SCR)70};71static struct sym_fwb_ofs sym_fw2b_ofs = {72 SYM_GEN_FW_B(struct SYM_FWB_SCR)73 SYM_GEN_B(struct SYM_FWB_SCR, start64)74 SYM_GEN_B(struct SYM_FWB_SCR, pm_handle)75};76static struct sym_fwz_ofs sym_fw2z_ofs = {77 SYM_GEN_FW_Z(struct SYM_FWZ_SCR)78};79#undef SYM_FWA_SCR80#undef SYM_FWB_SCR81#undef SYM_FWZ_SCR82 83#undef SYM_GEN_A84#undef SYM_GEN_B85#undef SYM_GEN_Z86#undef PADDR_A87#undef PADDR_B88 89#if SYM_CONF_GENERIC_SUPPORT90/*91 * Patch routine for firmware #1.92 */93static void94sym_fw1_patch(struct Scsi_Host *shost)95{96 struct sym_hcb *np = sym_get_hcb(shost);97 struct sym_fw1a_scr *scripta0;98 struct sym_fw1b_scr *scriptb0;99 100 scripta0 = (struct sym_fw1a_scr *) np->scripta0;101 scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;102 103 /*104 * Remove LED support if not needed.105 */106 if (!(np->features & FE_LED0)) {107 scripta0->idle[0] = cpu_to_scr(SCR_NO_OP);108 scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);109 scripta0->start[0] = cpu_to_scr(SCR_NO_OP);110 }111 112#ifdef SYM_CONF_IARB_SUPPORT113 /*114 * If user does not want to use IMMEDIATE ARBITRATION115 * when we are reselected while attempting to arbitrate,116 * patch the SCRIPTS accordingly with a SCRIPT NO_OP.117 */118 if (!SYM_CONF_SET_IARB_ON_ARB_LOST)119 scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);120#endif121 /*122 * Patch some data in SCRIPTS.123 * - start and done queue initial bus address.124 * - target bus address table bus address.125 */126 scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba);127 scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba);128 scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba);129}130#endif /* SYM_CONF_GENERIC_SUPPORT */131 132/*133 * Patch routine for firmware #2.134 */135static void136sym_fw2_patch(struct Scsi_Host *shost)137{138 struct sym_data *sym_data = shost_priv(shost);139 struct pci_dev *pdev = sym_data->pdev;140 struct sym_hcb *np = sym_data->ncb;141 struct sym_fw2a_scr *scripta0;142 struct sym_fw2b_scr *scriptb0;143 144 scripta0 = (struct sym_fw2a_scr *) np->scripta0;145 scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;146 147 /*148 * Remove LED support if not needed.149 */150 if (!(np->features & FE_LED0)) {151 scripta0->idle[0] = cpu_to_scr(SCR_NO_OP);152 scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);153 scripta0->start[0] = cpu_to_scr(SCR_NO_OP);154 }155 156#if SYM_CONF_DMA_ADDRESSING_MODE == 2157 /*158 * Remove useless 64 bit DMA specific SCRIPTS, 159 * when this feature is not available.160 */161 if (!use_dac(np)) {162 scripta0->is_dmap_dirty[0] = cpu_to_scr(SCR_NO_OP);163 scripta0->is_dmap_dirty[1] = 0;164 scripta0->is_dmap_dirty[2] = cpu_to_scr(SCR_NO_OP);165 scripta0->is_dmap_dirty[3] = 0;166 }167#endif168 169#ifdef SYM_CONF_IARB_SUPPORT170 /*171 * If user does not want to use IMMEDIATE ARBITRATION172 * when we are reselected while attempting to arbitrate,173 * patch the SCRIPTS accordingly with a SCRIPT NO_OP.174 */175 if (!SYM_CONF_SET_IARB_ON_ARB_LOST)176 scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);177#endif178 /*179 * Patch some variable in SCRIPTS.180 * - start and done queue initial bus address.181 * - target bus address table bus address.182 */183 scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba);184 scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba);185 scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba);186 187 /*188 * Remove the load of SCNTL4 on reselection if not a C10.189 */190 if (!(np->features & FE_C10)) {191 scripta0->resel_scntl4[0] = cpu_to_scr(SCR_NO_OP);192 scripta0->resel_scntl4[1] = cpu_to_scr(0);193 }194 195 /*196 * Remove a couple of work-arounds specific to C1010 if 197 * they are not desirable. See `sym_fw2.h' for more details.198 */199 if (!(pdev->device == PCI_DEVICE_ID_LSI_53C1010_66 &&200 pdev->revision < 0x1 &&201 np->pciclk_khz < 60000)) {202 scripta0->datao_phase[0] = cpu_to_scr(SCR_NO_OP);203 scripta0->datao_phase[1] = cpu_to_scr(0);204 }205 if (!(pdev->device == PCI_DEVICE_ID_LSI_53C1010_33 /* &&206 pdev->revision < 0xff */)) {207 scripta0->sel_done[0] = cpu_to_scr(SCR_NO_OP);208 scripta0->sel_done[1] = cpu_to_scr(0);209 }210 211 /*212 * Patch some other variables in SCRIPTS.213 * These ones are loaded by the SCRIPTS processor.214 */215 scriptb0->pm0_data_addr[0] =216 cpu_to_scr(np->scripta_ba + 217 offsetof(struct sym_fw2a_scr, pm0_data));218 scriptb0->pm1_data_addr[0] =219 cpu_to_scr(np->scripta_ba + 220 offsetof(struct sym_fw2a_scr, pm1_data));221}222 223/*224 * Fill the data area in scripts.225 * To be done for all firmwares.226 */227static void228sym_fw_fill_data (u32 *in, u32 *out)229{230 int i;231 232 for (i = 0; i < SYM_CONF_MAX_SG; i++) {233 *in++ = SCR_CHMOV_TBL ^ SCR_DATA_IN;234 *in++ = offsetof (struct sym_dsb, data[i]);235 *out++ = SCR_CHMOV_TBL ^ SCR_DATA_OUT;236 *out++ = offsetof (struct sym_dsb, data[i]);237 }238}239 240/*241 * Setup useful script bus addresses.242 * To be done for all firmwares.243 */244static void 245sym_fw_setup_bus_addresses(struct sym_hcb *np, struct sym_fw *fw)246{247 u32 *pa;248 u_short *po;249 int i;250 251 /*252 * Build the bus address table for script A 253 * from the script A offset table.254 */255 po = (u_short *) fw->a_ofs;256 pa = (u32 *) &np->fwa_bas;257 for (i = 0 ; i < sizeof(np->fwa_bas)/sizeof(u32) ; i++)258 pa[i] = np->scripta_ba + po[i];259 260 /*261 * Same for script B.262 */263 po = (u_short *) fw->b_ofs;264 pa = (u32 *) &np->fwb_bas;265 for (i = 0 ; i < sizeof(np->fwb_bas)/sizeof(u32) ; i++)266 pa[i] = np->scriptb_ba + po[i];267 268 /*269 * Same for script Z.270 */271 po = (u_short *) fw->z_ofs;272 pa = (u32 *) &np->fwz_bas;273 for (i = 0 ; i < sizeof(np->fwz_bas)/sizeof(u32) ; i++)274 pa[i] = np->scriptz_ba + po[i];275}276 277#if SYM_CONF_GENERIC_SUPPORT278/*279 * Setup routine for firmware #1.280 */281static void 282sym_fw1_setup(struct sym_hcb *np, struct sym_fw *fw)283{284 struct sym_fw1a_scr *scripta0;285 286 scripta0 = (struct sym_fw1a_scr *) np->scripta0;287 288 /*289 * Fill variable parts in scripts.290 */291 sym_fw_fill_data(scripta0->data_in, scripta0->data_out);292 293 /*294 * Setup bus addresses used from the C code..295 */296 sym_fw_setup_bus_addresses(np, fw);297}298#endif /* SYM_CONF_GENERIC_SUPPORT */299 300/*301 * Setup routine for firmware #2.302 */303static void 304sym_fw2_setup(struct sym_hcb *np, struct sym_fw *fw)305{306 struct sym_fw2a_scr *scripta0;307 308 scripta0 = (struct sym_fw2a_scr *) np->scripta0;309 310 /*311 * Fill variable parts in scripts.312 */313 sym_fw_fill_data(scripta0->data_in, scripta0->data_out);314 315 /*316 * Setup bus addresses used from the C code..317 */318 sym_fw_setup_bus_addresses(np, fw);319}320 321/*322 * Allocate firmware descriptors.323 */324#if SYM_CONF_GENERIC_SUPPORT325static struct sym_fw sym_fw1 = SYM_FW_ENTRY(sym_fw1, "NCR-generic");326#endif /* SYM_CONF_GENERIC_SUPPORT */327static struct sym_fw sym_fw2 = SYM_FW_ENTRY(sym_fw2, "LOAD/STORE-based");328 329/*330 * Find the most appropriate firmware for a chip.331 */332struct sym_fw * 333sym_find_firmware(struct sym_chip *chip)334{335 if (chip->features & FE_LDSTR)336 return &sym_fw2;337#if SYM_CONF_GENERIC_SUPPORT338 else if (!(chip->features & (FE_PFEN|FE_NOPM|FE_DAC)))339 return &sym_fw1;340#endif341 else342 return NULL;343}344 345/*346 * Bind a script to physical addresses.347 */348void sym_fw_bind_script(struct sym_hcb *np, u32 *start, int len)349{350 u32 opcode, new, old, tmp1, tmp2;351 u32 *end, *cur;352 int relocs;353 354 cur = start;355 end = start + len/4;356 357 while (cur < end) {358 359 opcode = *cur;360 361 /*362 * If we forget to change the length363 * in scripts, a field will be364 * padded with 0. This is an illegal365 * command.366 */367 if (opcode == 0) {368 printf ("%s: ERROR0 IN SCRIPT at %d.\n",369 sym_name(np), (int) (cur-start));370 ++cur;371 continue;372 }373 374 /*375 * We use the bogus value 0xf00ff00f ;-)376 * to reserve data area in SCRIPTS.377 */378 if (opcode == SCR_DATA_ZERO) {379 *cur++ = 0;380 continue;381 }382 383 if (DEBUG_FLAGS & DEBUG_SCRIPT)384 printf ("%d: <%x>\n", (int) (cur-start),385 (unsigned)opcode);386 387 /*388 * We don't have to decode ALL commands389 */390 switch (opcode >> 28) {391 case 0xf:392 /*393 * LOAD / STORE DSA relative, don't relocate.394 */395 relocs = 0;396 break;397 case 0xe:398 /*399 * LOAD / STORE absolute.400 */401 relocs = 1;402 break;403 case 0xc:404 /*405 * COPY has TWO arguments.406 */407 relocs = 2;408 tmp1 = cur[1];409 tmp2 = cur[2];410 if ((tmp1 ^ tmp2) & 3) {411 printf ("%s: ERROR1 IN SCRIPT at %d.\n",412 sym_name(np), (int) (cur-start));413 }414 /*415 * If PREFETCH feature not enabled, remove 416 * the NO FLUSH bit if present.417 */418 if ((opcode & SCR_NO_FLUSH) &&419 !(np->features & FE_PFEN)) {420 opcode = (opcode & ~SCR_NO_FLUSH);421 }422 break;423 case 0x0:424 /*425 * MOVE/CHMOV (absolute address)426 */427 if (!(np->features & FE_WIDE))428 opcode = (opcode | OPC_MOVE);429 relocs = 1;430 break;431 case 0x1:432 /*433 * MOVE/CHMOV (table indirect)434 */435 if (!(np->features & FE_WIDE))436 opcode = (opcode | OPC_MOVE);437 relocs = 0;438 break;439#ifdef SYM_CONF_TARGET_ROLE_SUPPORT440 case 0x2:441 /*442 * MOVE/CHMOV in target role (absolute address)443 */444 opcode &= ~0x20000000;445 if (!(np->features & FE_WIDE))446 opcode = (opcode & ~OPC_TCHMOVE);447 relocs = 1;448 break;449 case 0x3:450 /*451 * MOVE/CHMOV in target role (table indirect)452 */453 opcode &= ~0x20000000;454 if (!(np->features & FE_WIDE))455 opcode = (opcode & ~OPC_TCHMOVE);456 relocs = 0;457 break;458#endif459 case 0x8:460 /*461 * JUMP / CALL462 * don't relocate if relative :-)463 */464 if (opcode & 0x00800000)465 relocs = 0;466 else if ((opcode & 0xf8400000) == 0x80400000)/*JUMP64*/467 relocs = 2;468 else469 relocs = 1;470 break;471 case 0x4:472 case 0x5:473 case 0x6:474 case 0x7:475 relocs = 1;476 break;477 default:478 relocs = 0;479 break;480 }481 482 /*483 * Scriptify:) the opcode.484 */485 *cur++ = cpu_to_scr(opcode);486 487 /*488 * If no relocation, assume 1 argument 489 * and just scriptize:) it.490 */491 if (!relocs) {492 *cur = cpu_to_scr(*cur);493 ++cur;494 continue;495 }496 497 /*498 * Otherwise performs all needed relocations.499 */500 while (relocs--) {501 old = *cur;502 503 switch (old & RELOC_MASK) {504 case RELOC_REGISTER:505 new = (old & ~RELOC_MASK) + np->mmio_ba;506 break;507 case RELOC_LABEL_A:508 new = (old & ~RELOC_MASK) + np->scripta_ba;509 break;510 case RELOC_LABEL_B:511 new = (old & ~RELOC_MASK) + np->scriptb_ba;512 break;513 case RELOC_SOFTC:514 new = (old & ~RELOC_MASK) + np->hcb_ba;515 break;516 case 0:517 /*518 * Don't relocate a 0 address.519 * They are mostly used for patched or 520 * script self-modified areas.521 */522 if (old == 0) {523 new = old;524 break;525 }526 fallthrough;527 default:528 new = 0;529 panic("sym_fw_bind_script: "530 "weird relocation %x\n", old);531 break;532 }533 534 *cur++ = cpu_to_scr(new);535 }536 }537}538