예제 #1
0
void __octeon_board_get_clock_info(uint32_t def_ddr_clock_mhz)
{
	uint8_t ee_buf[OCTEON_EEPROM_MAX_TUPLE_LENGTH] __attribute__((unused));
	int addr __attribute__((unused));
	/* Assume no descriptor is present */
	gd->mem_clk = def_ddr_clock_mhz;

	debug("%s(%u)\n", __func__, def_ddr_clock_mhz);
	/* Initialize DDR reference frequency if not already set. */
	if (!gd->arch.ddr_ref_hertz)
		gd->arch.ddr_ref_hertz = 50000000;

#if USE_EEPROM
	/* OCTEON I and OCTEON Plus use the old clock descriptor of which
	 * there are two versions.  OCTEON II uses a dedicated DDR clock
	 * descriptor instead.
	 */
	if (OCTEON_IS_OCTEON1PLUS()) {
		octeon_eeprom_header_t *header;
		struct octeon_eeprom_clock_desc_v1 *clock_v1;
		struct octeon_eeprom_clock_desc_v2 *clock_v2;

		addr = octeon_tlv_get_tuple_addr(CONFIG_SYS_DEF_EEPROM_ADDR,
						 EEPROM_CLOCK_DESC_TYPE, 0,
						 ee_buf,
						 OCTEON_EEPROM_MAX_TUPLE_LENGTH);
		if (addr < 0)
			return;

		header = (octeon_eeprom_header_t *)ee_buf;
		switch (header->major_version) {
		case 1:
			clock_v1 = (struct octeon_eeprom_clock_desc_v1 *)ee_buf;
			gd->mem_clk = clock_v1->ddr_clock_mhz;
			break;
		case 2:
			clock_v2 = (struct octeon_eeprom_clock_desc_v2 *)ee_buf;
			gd->mem_clk = clock_v2->ddr_clock_mhz;
			break;
		default:
			printf("Unknown TLV clock header version %d.%d\n",
			       header->major_version, header->minor_version);
		}
	} else {
		octeon_eeprom_ddr_clock_desc_t *ddr_clock_ptr;
		addr = octeon_tlv_get_tuple_addr(CONFIG_SYS_DEF_EEPROM_ADDR,
						 EEPROM_DDR_CLOCK_DESC_TYPE, 0,
						 ee_buf,
						 OCTEON_EEPROM_MAX_TUPLE_LENGTH);
		if (addr < 0)
			return;


		ddr_clock_ptr = (octeon_eeprom_ddr_clock_desc_t *)ee_buf;
		gd->mem_clk = ddr_clock_ptr->ddr_clock_hz / 1000000;
	}
	if (gd->mem_clk < 100 || gd->mem_clk > 2000)
		gd->mem_clk = def_ddr_clock_mhz;
#endif
}
static int octeon_l2c_probe(struct platform_device *pdev)
{
	struct edac_device_ctl_info *l2c;

	int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1;

	/* 'Tags' are block 0, 'Data' is block 1*/
	l2c = edac_device_alloc_ctl_info(0, "l2c", num_tads, "l2c", 2, 0,
					 NULL, 0, edac_device_alloc_index());
	if (!l2c)
		return -ENOMEM;

	l2c->dev = &pdev->dev;
	platform_set_drvdata(pdev, l2c);
	l2c->dev_name = dev_name(&pdev->dev);

	l2c->mod_name = "octeon-l2c";
	l2c->ctl_name = "octeon_l2c_err";


	if (OCTEON_IS_OCTEON1PLUS()) {
		union cvmx_l2t_err l2t_err;
		union cvmx_l2d_err l2d_err;

		l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
		l2t_err.s.sec_intena = 0;	/* We poll */
		l2t_err.s.ded_intena = 0;
		cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);

		l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR);
		l2d_err.s.sec_intena = 0;	/* We poll */
		l2d_err.s.ded_intena = 0;
		cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64);

		l2c->edac_check = octeon_l2c_poll_oct1;
	} else {
		/* OCTEON II */
		l2c->edac_check = octeon_l2c_poll_oct2;
	}

	if (edac_device_add_device(l2c) > 0) {
		pr_err("%s: edac_device_add_device() failed\n", __func__);
		goto err;
	}


	return 0;

err:
	edac_device_free_ctl_info(l2c);

	return -ENXIO;
}
예제 #3
0
/**
 * Load the QLM JTAG chain with data from all lanes of the QLM.
 *
 * @param qlm    QLM to program
 */
void cvmx_helper_qlm_jtag_capture(int qlm)
{
    union cvmx_ciu_qlm_jtgc jtgc;
    union cvmx_ciu_qlm_jtgd jtgd;

    jtgc.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGC);
    jtgc.s.mux_sel = qlm;
    if (OCTEON_IS_OCTEON1PLUS())
        jtgc.s.bypass = 1 << qlm;

    cvmx_write_csr(CVMX_CIU_QLM_JTGC, jtgc.u64);
    cvmx_read_csr(CVMX_CIU_QLM_JTGC);

    jtgd.u64 = 0;
    jtgd.s.capture = 1;
    if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
        jtgd.s.select = 1 << qlm;
    cvmx_write_csr(CVMX_CIU_QLM_JTGD, jtgd.u64);
    do {
        jtgd.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGD);
    } while (jtgd.s.capture);
}
예제 #4
0
/**
 * Write up to 32bits into the QLM jtag chain. Bits are shifted
 * into the MSB and out the LSB, so you should shift in the low
 * order bits followed by the high order bits. The JTAG chain for
 * CN52XX and CN56XX is 4 * 268 bits long, or 1072. The JTAG chain
 * for CN63XX is 4 * 300 bits long, or 1200.
 *
 * @param qlm    QLM to shift value into
 * @param bits   Number of bits to shift in (1-32).
 * @param data   Data to shift in. Bit 0 enters the chain first, followed by
 *               bit 1, etc.
 *
 * @return The low order bits of the JTAG chain that shifted out of the
 *         circle.
 */
uint32_t cvmx_helper_qlm_jtag_shift(int qlm, int bits, uint32_t data)
{
    union cvmx_ciu_qlm_jtgc jtgc;
    union cvmx_ciu_qlm_jtgd jtgd;

    jtgc.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGC);
    jtgc.s.mux_sel = qlm;
    if (OCTEON_IS_OCTEON1PLUS())
        jtgc.s.bypass = 1 << qlm;
    cvmx_write_csr(CVMX_CIU_QLM_JTGC, jtgc.u64);
    cvmx_read_csr(CVMX_CIU_QLM_JTGC);

    jtgd.u64 = 0;
    jtgd.s.shift = 1;
    jtgd.s.shft_cnt = bits - 1;
    jtgd.s.shft_reg = data;
    if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X))
        jtgd.s.select = 1 << qlm;
    cvmx_write_csr(CVMX_CIU_QLM_JTGD, jtgd.u64);
    do {
        jtgd.u64 = cvmx_read_csr(CVMX_CIU_QLM_JTGD);
    } while (jtgd.s.shift);
    return jtgd.s.shft_reg >> (32 - bits);
}
예제 #5
0
void __octeon_board_create_random_mac_addr(void)
{
#ifndef CONFIG_OCTEON_SIM
	uint8_t fuse_buf[128];
	cvmx_mio_fus_rcmd_t fus_rcmd;
	uint32_t poly = 0x04c11db7;
	uint32_t crc = 0xffffffff;
	uint64_t *ptr;
	int ser_len = strlen((char *)gd->arch.board_desc.serial_str);
	int i;

	memset(fuse_buf, 0, sizeof(fuse_buf));
	fuse_buf[0] = gd->arch.board_desc.board_type;
	fuse_buf[1] = (gd->arch.board_desc.rev_major << 4)
			| gd->arch.board_desc.rev_minor;
	fuse_buf[2] = ser_len;
	strncpy((char*)(fuse_buf+3), (char*)gd->arch.board_desc.serial_str,
		sizeof(fuse_buf)-3);

	/* For a random number we perform a CRC32 using the board type,
	 * revision, serial number length, serial number and for OCTEON 2 and 3
	 * the fuse settings.
	 */

	CVMX_MT_CRC_POLYNOMIAL(poly);
	CVMX_ES32(crc, crc);
	CVMX_MT_CRC_IV_REFLECT(crc);
	ptr = (uint64_t *)fuse_buf;
	for (i = 0; i < sizeof(fuse_buf); i += 8)
		CVMX_MT_CRC_DWORD_REFLECT(*ptr++);

	if (!OCTEON_IS_OCTEON1PLUS()) {
		fus_rcmd.u64 = 0;
		fus_rcmd.s.pend = 1;
		for (i = 0; i < sizeof(fuse_buf); i++) {
			do {
				fus_rcmd.u64 = cvmx_read_csr(CVMX_MIO_FUS_RCMD);
			} while (fus_rcmd.s.pend == 1);
			fuse_buf[i] = fus_rcmd.s.dat;
		}
		for (i = 0; i < sizeof(fuse_buf); i += 8)
			CVMX_MT_CRC_DWORD_REFLECT(*ptr++);
	}
	/* Get the final CRC32 */
	CVMX_MF_CRC_IV_REFLECT(crc);
	crc ^= 0xffffffff;

	gd->arch.mac_desc.count = 255;
	gd->arch.mac_desc.mac_addr_base[0] = 0x02;	/* locally administered */
	gd->arch.mac_desc.mac_addr_base[1] = crc & 0xff;
	gd->arch.mac_desc.mac_addr_base[2] = (crc >> 8) & 0xff;
	gd->arch.mac_desc.mac_addr_base[3] = (crc >> 16) & 0xff;
	gd->arch.mac_desc.mac_addr_base[4] = (crc >> 24) & 0xff;
	gd->arch.mac_desc.mac_addr_base[5] = 0;
	debug("Created random MAC address %pM", gd->arch.mac_desc.mac_addr_base);
#else
	gd->arch.mac_desc.mac_addr_base[0] = 2;
	gd->arch.mac_desc.mac_addr_base[1] = 0x00;
	gd->arch.mac_desc.mac_addr_base[2] = 0xDE;
	gd->arch.mac_desc.mac_addr_base[3] = 0xAD;
	gd->arch.mac_desc.mac_addr_base[4] = 0xBF;
	gd->arch.mac_desc.mac_addr_base[5] = 0x00;
	gd->arch.mac_desc.count = 255;
#endif
}
예제 #6
0
static int octeon_lmc_edac_probe(struct platform_device *pdev)
{
	struct mem_ctl_info *mci;
	struct edac_mc_layer layers[1];
	int mc = pdev->id;

	opstate_init();

	layers[0].type = EDAC_MC_LAYER_CHANNEL;
	layers[0].size = 1;
	layers[0].is_virt_csrow = false;

	if (OCTEON_IS_OCTEON1PLUS()) {
		union cvmx_lmcx_mem_cfg0 cfg0;

		cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0));
		if (!cfg0.s.ecc_ena) {
			dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
			return 0;
		}

		mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, sizeof(struct octeon_lmc_pvt));
		if (!mci)
			return -ENXIO;

		mci->pdev = &pdev->dev;
		mci->dev_name = dev_name(&pdev->dev);

		mci->mod_name = "octeon-lmc";
		mci->ctl_name = "octeon-lmc-err";
		mci->edac_check = octeon_lmc_edac_poll;

		if (edac_mc_add_mc_with_groups(mci, octeon_dev_groups)) {
			dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
			edac_mc_free(mci);
			return -ENXIO;
		}

		cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
		cfg0.s.intr_ded_ena = 0;	/* We poll */
		cfg0.s.intr_sec_ena = 0;
		cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64);
	} else {
		/* OCTEON II */
		union cvmx_lmcx_int_en en;
		union cvmx_lmcx_config config;

		config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0));
		if (!config.s.ecc_ena) {
			dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
			return 0;
		}

		mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, sizeof(struct octeon_lmc_pvt));
		if (!mci)
			return -ENXIO;

		mci->pdev = &pdev->dev;
		mci->dev_name = dev_name(&pdev->dev);

		mci->mod_name = "octeon-lmc";
		mci->ctl_name = "co_lmc_err";
		mci->edac_check = octeon_lmc_edac_poll_o2;

		if (edac_mc_add_mc_with_groups(mci, octeon_dev_groups)) {
			dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
			edac_mc_free(mci);
			return -ENXIO;
		}

		en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
		en.s.intr_ded_ena = 0;	/* We poll */
		en.s.intr_sec_ena = 0;
		cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64);
	}
	platform_set_drvdata(pdev, mci);

	return 0;
}
/* Version of octeon_model_get_string() that takes buffer as argument, as
** running early in u-boot static/global variables don't work when running from
** flash
*/
const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
{
	const char *family;
	const char *core_model;
	char pass[4];
#ifndef CVMX_BUILD_FOR_UBOOT
	int clock_mhz;
#endif
	const char *suffix;
	cvmx_l2d_fus3_t fus3;
	int num_cores;
	cvmx_mio_fus_dat2_t fus_dat2;
	cvmx_mio_fus_dat3_t fus_dat3;
	char fuse_model[10];
	char fuse_suffix[4] = {0};
	uint64_t fuse_data = 0;

	fus3.u64 = 0;
	if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX))
		fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3);
	fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2);
	fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);
	if (OCTEON_IS_MODEL(OCTEON_CN78XX))
		num_cores = cvmx_pop(cvmx_read_csr(CVMX_CIU3_FUSE));
	else
		num_cores = cvmx_pop(cvmx_read_csr(CVMX_CIU_FUSE));

	/* Make sure the non existent devices look disabled */
	switch ((chip_id >> 8) & 0xff) {
	case 6:		/* CN50XX */
	case 2:		/* CN30XX */
		fus_dat3.s.nodfa_dte = 1;
		fus_dat3.s.nozip = 1;
		break;
	case 4:		/* CN57XX or CN56XX */
		fus_dat3.s.nodfa_dte = 1;
		break;
	default:
		break;
	}

	/* Make a guess at the suffix */
	/* NSP = everything */
	/* EXP = No crypto */
	/* SCP = No DFA, No zip */
	/* CP = No DFA, No crypto, No zip */
	if (fus_dat3.s.nodfa_dte) {
		if (fus_dat2.s.nocrypto)
			suffix = "CP";
		else
			suffix = "SCP";
	} else if (fus_dat2.s.nocrypto)
		suffix = "EXP";
	else
		suffix = "NSP";

	/* Assume pass number is encoded using <5:3><2:0>. Exceptions will be
	   fixed later */
	sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7);

	/* Use the number of cores to determine the last 2 digits of the model
	   number. There are some exceptions that are fixed later */
	switch (num_cores) {
	case 32:
		core_model = "80";
		break;
	case 24:
		core_model = "70";
		break;
	case 16:
		core_model = "60";
		break;
	case 15:
		core_model = "58";
		break;
	case 14:
		core_model = "55";
		break;
	case 13:
		core_model = "52";
		break;
	case 12:
		core_model = "50";
		break;
	case 11:
		core_model = "48";
		break;
	case 10:
		core_model = "45";
		break;
	case 9:
		core_model = "42";
		break;
	case 8:
		core_model = "40";
		break;
	case 7:
		core_model = "38";
		break;
	case 6:
		core_model = "34";
		break;
	case 5:
		core_model = "32";
		break;
	case 4:
		core_model = "30";
		break;
	case 3:
		core_model = "25";
		break;
	case 2:
		core_model = "20";
		break;
	case 1:
		core_model = "10";
		break;
	default:
		core_model = "XX";
		break;
	}

	/* Now figure out the family, the first two digits */
	switch ((chip_id >> 8) & 0xff) {
	case 0:		/* CN38XX, CN37XX or CN36XX */
		if (fus3.cn38xx.crip_512k) {
			/* For some unknown reason, the 16 core one is called 37 instead of 36 */
			if (num_cores >= 16)
				family = "37";
			else
				family = "36";
		} else
			family = "38";
		/* This series of chips didn't follow the standard pass numbering */
		switch (chip_id & 0xf) {
		case 0:
			strcpy(pass, "1.X");
			break;
		case 1:
			strcpy(pass, "2.X");
			break;
		case 3:
			strcpy(pass, "3.X");
			break;
		default:
			strcpy(pass, "X.X");
			break;
		}
		break;
	case 1:		/* CN31XX or CN3020 */
		if ((chip_id & 0x10) || fus3.cn31xx.crip_128k)
			family = "30";
		else
			family = "31";
		/* This series of chips didn't follow the standard pass numbering */
		switch (chip_id & 0xf) {
		case 0:
			strcpy(pass, "1.0");
			break;
		case 2:
			strcpy(pass, "1.1");
			break;
		default:
			strcpy(pass, "X.X");
			break;
		}
		break;
	case 2:		/* CN3010 or CN3005 */
		family = "30";
		/* A chip with half cache is an 05 */
		if (fus3.cn30xx.crip_64k)
			core_model = "05";
		/* This series of chips didn't follow the standard pass numbering */
		switch (chip_id & 0xf) {
		case 0:
			strcpy(pass, "1.0");
			break;
		case 2:
			strcpy(pass, "1.1");
			break;
		default:
			strcpy(pass, "X.X");
			break;
		}
		break;
	case 3:		/* CN58XX */
		family = "58";
		/* Special case. 4 core, half cache (CP with half cache) */
		if ((num_cores == 4) && fus3.cn58xx.crip_1024k && !strncmp(suffix, "CP", 2))
			core_model = "29";

		/* Pass 1 uses different encodings for pass numbers */
		if ((chip_id & 0xFF) < 0x8) {
			switch (chip_id & 0x3) {
			case 0:
				strcpy(pass, "1.0");
				break;
			case 1:
				strcpy(pass, "1.1");
				break;
			case 3:
				strcpy(pass, "1.2");
				break;
			default:
				strcpy(pass, "1.X");
				break;
			}
		}
		break;
	case 4:		/* CN57XX, CN56XX, CN55XX, CN54XX */
		if (fus_dat2.cn56xx.raid_en) {
			if (fus3.cn56xx.crip_1024k)
				family = "55";
			else
				family = "57";
			if (fus_dat2.cn56xx.nocrypto)
				suffix = "SP";
			else
				suffix = "SSP";
		} else {
			if (fus_dat2.cn56xx.nocrypto)
				suffix = "CP";
			else {
				suffix = "NSP";
				if (fus_dat3.s.nozip)
					suffix = "SCP";

				if (fus_dat3.cn56xx.bar2_en)
					suffix = "NSPB2";
			}
			if (fus3.cn56xx.crip_1024k)
				family = "54";
			else
				family = "56";
		}
		break;
	case 6:		/* CN50XX */
		family = "50";
		break;
	case 7:		/* CN52XX */
		if (fus3.cn52xx.crip_256k)
			family = "51";
		else
			family = "52";
		break;
	case 0x93:		/* CN61XX/CN60XX */
		family = "61";
		if (fus_dat3.cn63xx.l2c_crip == 2)
			family = "60";
		if (fus_dat3.cn61xx.nozip)
			suffix = "SCP";
		else
			suffix = "AAP";
		break;
	case 0x90:		/* CN63XX/CN62XX */
		family = "63";
		if (fus_dat3.s.l2c_crip == 2)
			family = "62";
		if (num_cores == 6)	/* Other core counts match generic */
			core_model = "35";
		if (fus_dat2.cn63xx.nocrypto)
			suffix = "CP";
		else if (fus_dat2.cn63xx.dorm_crypto)
			suffix = "DAP";
		else if (fus_dat3.cn63xx.nozip)
			suffix = "SCP";
		else
			suffix = "AAP";
		break;
	case 0x92:		/* CN66XX */
		family = "66";
		if (num_cores == 6)	/* Other core counts match generic */
			core_model = "35";
		if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto)
			suffix = "AP";
		if (fus_dat2.cn66xx.nocrypto)
			suffix = "CP";
		else if (fus_dat2.cn66xx.dorm_crypto)
			suffix = "DAP";
		else if (fus_dat3.cn66xx.nozip && fus_dat2.cn66xx.raid_en)
			suffix = "SCP";
		else if (!fus_dat2.cn66xx.raid_en)
			suffix = "HAP";
		else
			suffix = "AAP";
		break;
	case 0x91:		/* CN68XX */
		family = "68";
		if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn68xx.nozip)
			suffix = "CP";
		else if (fus_dat2.cn68xx.dorm_crypto)
			suffix = "DAP";
		else if (fus_dat3.cn68xx.nozip)
			suffix = "SCP";
		else if (fus_dat2.cn68xx.nocrypto)
			suffix = "SP";
		else if (!fus_dat2.cn68xx.raid_en)
			suffix = "HAP";
		else
			suffix = "AAP";
		break;
	case 0x94:		/* CNF71XX */
		family = "F71";
		if (fus_dat3.cnf71xx.nozip)
			suffix = "SCP";
		else
			suffix = "AAP";
		break;
	case 0x95:		/* CN78XX */
		family = "78";
		if (fus_dat3.cn70xx.nozip)
			suffix = "SCP";
		else
			suffix = "AAP";
		break;
	case 0x96:		/* CN70XX */
		family = "70";
		if (cvmx_read_csr(CVMX_MIO_FUS_PDF) & (0x1ULL << 32))
			family = "71";
		if (fus_dat2.cn70xx.nocrypto)
			suffix = "CP";
		else if (fus_dat3.cn70xx.nodfa_dte)
			suffix = "SCP";
		else
			suffix = "AAP";
		break;
	default:
		family = "XX";
		core_model = "XX";
		strcpy(pass, "X.X");
		suffix = "XXX";
		break;
	}

#ifndef CVMX_BUILD_FOR_UBOOT
	clock_mhz = cvmx_clock_get_rate(CVMX_CLOCK_RCLK) / 1000000;
#endif

	if (family[0] != '3') {
		if (OCTEON_IS_OCTEON1PLUS() || OCTEON_IS_OCTEON2()) {
			int fuse_base = 384 / 8;
			if (family[0] == '6' || OCTEON_IS_OCTEON3())
				fuse_base = 832 / 8;
			/* Check for model in fuses, overrides normal decode */
			/* This is _not_ valid for Octeon CN3XXX models */
			fuse_data |= cvmx_fuse_read_byte(fuse_base + 5);
			fuse_data = fuse_data << 8;
			fuse_data |= cvmx_fuse_read_byte(fuse_base + 4);
			fuse_data = fuse_data << 8;
			fuse_data |= cvmx_fuse_read_byte(fuse_base + 3);
			fuse_data = fuse_data << 8;
			fuse_data |= cvmx_fuse_read_byte(fuse_base + 2);
			fuse_data = fuse_data << 8;
			fuse_data |= cvmx_fuse_read_byte(fuse_base + 1);
			fuse_data = fuse_data << 8;
			fuse_data |= cvmx_fuse_read_byte(fuse_base);
			if (fuse_data & 0x7ffff) {
				int model = fuse_data & 0x3fff;
				int suffix = (fuse_data >> 14) & 0x1f;
				if (suffix && model) {      /* Have both number and suffix in fuses, so both */
					sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1);
					core_model = "";
					family = fuse_model;
				} else if (suffix && !model) {      /* Only have suffix, so add suffix to 'normal' model number */
					sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1);
					core_model = fuse_model;
				} else {    /* Don't have suffix, so just use model from fuses */

					sprintf(fuse_model, "%d", model);
					core_model = "";
					family = fuse_model;
				}
			}
		} else {