162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Aic94xx SAS/SATA driver access to shared data structures and memory
462306a36Sopenharmony_ci * maps.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
762306a36Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "aic94xx.h"
1562306a36Sopenharmony_ci#include "aic94xx_reg.h"
1662306a36Sopenharmony_ci#include "aic94xx_sds.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* ---------- OCM stuff ---------- */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct asd_ocm_dir_ent {
2162306a36Sopenharmony_ci	u8 type;
2262306a36Sopenharmony_ci	u8 offs[3];
2362306a36Sopenharmony_ci	u8 _r1;
2462306a36Sopenharmony_ci	u8 size[3];
2562306a36Sopenharmony_ci} __attribute__ ((packed));
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct asd_ocm_dir {
2862306a36Sopenharmony_ci	char sig[2];
2962306a36Sopenharmony_ci	u8   _r1[2];
3062306a36Sopenharmony_ci	u8   major;          /* 0 */
3162306a36Sopenharmony_ci	u8   minor;          /* 0 */
3262306a36Sopenharmony_ci	u8   _r2;
3362306a36Sopenharmony_ci	u8   num_de;
3462306a36Sopenharmony_ci	struct asd_ocm_dir_ent entry[15];
3562306a36Sopenharmony_ci} __attribute__ ((packed));
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define	OCM_DE_OCM_DIR			0x00
3862306a36Sopenharmony_ci#define	OCM_DE_WIN_DRVR			0x01
3962306a36Sopenharmony_ci#define	OCM_DE_BIOS_CHIM		0x02
4062306a36Sopenharmony_ci#define	OCM_DE_RAID_ENGN		0x03
4162306a36Sopenharmony_ci#define	OCM_DE_BIOS_INTL		0x04
4262306a36Sopenharmony_ci#define	OCM_DE_BIOS_CHIM_OSM		0x05
4362306a36Sopenharmony_ci#define	OCM_DE_BIOS_CHIM_DYNAMIC	0x06
4462306a36Sopenharmony_ci#define	OCM_DE_ADDC2C_RES0		0x07
4562306a36Sopenharmony_ci#define	OCM_DE_ADDC2C_RES1		0x08
4662306a36Sopenharmony_ci#define	OCM_DE_ADDC2C_RES2		0x09
4762306a36Sopenharmony_ci#define	OCM_DE_ADDC2C_RES3		0x0A
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define OCM_INIT_DIR_ENTRIES	5
5062306a36Sopenharmony_ci/***************************************************************************
5162306a36Sopenharmony_ci*  OCM directory default
5262306a36Sopenharmony_ci***************************************************************************/
5362306a36Sopenharmony_cistatic struct asd_ocm_dir OCMDirInit =
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	.sig = {0x4D, 0x4F},	/* signature */
5662306a36Sopenharmony_ci	.num_de = OCM_INIT_DIR_ENTRIES,	/* no. of directory entries */
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/***************************************************************************
6062306a36Sopenharmony_ci*  OCM directory Entries default
6162306a36Sopenharmony_ci***************************************************************************/
6262306a36Sopenharmony_cistatic struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] =
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	{
6562306a36Sopenharmony_ci		.type = (OCM_DE_ADDC2C_RES0),	/* Entry type  */
6662306a36Sopenharmony_ci		.offs = {128},			/* Offset */
6762306a36Sopenharmony_ci		.size = {0, 4},			/* size */
6862306a36Sopenharmony_ci	},
6962306a36Sopenharmony_ci	{
7062306a36Sopenharmony_ci		.type = (OCM_DE_ADDC2C_RES1),	/* Entry type  */
7162306a36Sopenharmony_ci		.offs = {128, 4},		/* Offset */
7262306a36Sopenharmony_ci		.size = {0, 4},			/* size */
7362306a36Sopenharmony_ci	},
7462306a36Sopenharmony_ci	{
7562306a36Sopenharmony_ci		.type = (OCM_DE_ADDC2C_RES2),	/* Entry type  */
7662306a36Sopenharmony_ci		.offs = {128, 8},		/* Offset */
7762306a36Sopenharmony_ci		.size = {0, 4},			/* size */
7862306a36Sopenharmony_ci	},
7962306a36Sopenharmony_ci	{
8062306a36Sopenharmony_ci		.type = (OCM_DE_ADDC2C_RES3),	/* Entry type  */
8162306a36Sopenharmony_ci		.offs = {128, 12},		/* Offset */
8262306a36Sopenharmony_ci		.size = {0, 4},			/* size */
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{
8562306a36Sopenharmony_ci		.type = (OCM_DE_WIN_DRVR),	/* Entry type  */
8662306a36Sopenharmony_ci		.offs = {128, 16},		/* Offset */
8762306a36Sopenharmony_ci		.size = {128, 235, 1},		/* size */
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistruct asd_bios_chim_struct {
9262306a36Sopenharmony_ci	char sig[4];
9362306a36Sopenharmony_ci	u8   major;          /* 1 */
9462306a36Sopenharmony_ci	u8   minor;          /* 0 */
9562306a36Sopenharmony_ci	u8   bios_major;
9662306a36Sopenharmony_ci	u8   bios_minor;
9762306a36Sopenharmony_ci	__le32  bios_build;
9862306a36Sopenharmony_ci	u8   flags;
9962306a36Sopenharmony_ci	u8   pci_slot;
10062306a36Sopenharmony_ci	__le16  ue_num;
10162306a36Sopenharmony_ci	__le16  ue_size;
10262306a36Sopenharmony_ci	u8  _r[14];
10362306a36Sopenharmony_ci	/* The unit element array is right here.
10462306a36Sopenharmony_ci	 */
10562306a36Sopenharmony_ci} __attribute__ ((packed));
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/**
10862306a36Sopenharmony_ci * asd_read_ocm_seg - read an on chip memory (OCM) segment
10962306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
11062306a36Sopenharmony_ci * @buffer: where to write the read data
11162306a36Sopenharmony_ci * @offs: offset into OCM where to read from
11262306a36Sopenharmony_ci * @size: how many bytes to read
11362306a36Sopenharmony_ci *
11462306a36Sopenharmony_ci * Return the number of bytes not read. Return 0 on success.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistatic int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
11762306a36Sopenharmony_ci			    u32 offs, int size)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	u8 *p = buffer;
12062306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
12162306a36Sopenharmony_ci		asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
12262306a36Sopenharmony_ci	else {
12362306a36Sopenharmony_ci		for ( ; size > 0; size--, offs++, p++)
12462306a36Sopenharmony_ci			*p = asd_read_ocm_byte(asd_ha, offs);
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	return size;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int asd_read_ocm_dir(struct asd_ha_struct *asd_ha,
13062306a36Sopenharmony_ci			    struct asd_ocm_dir *dir, u32 offs)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir));
13362306a36Sopenharmony_ci	if (err) {
13462306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read ocm segment\n");
13562306a36Sopenharmony_ci		return err;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (dir->sig[0] != 'M' || dir->sig[1] != 'O') {
13962306a36Sopenharmony_ci		ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n",
14062306a36Sopenharmony_ci			    dir->sig[0], dir->sig[1]);
14162306a36Sopenharmony_ci		return -ENOENT;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	if (dir->major != 0) {
14462306a36Sopenharmony_ci		asd_printk("unsupported major version of ocm dir:0x%x\n",
14562306a36Sopenharmony_ci			   dir->major);
14662306a36Sopenharmony_ci		return -ENOENT;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	dir->num_de &= 0xf;
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/**
15362306a36Sopenharmony_ci * asd_write_ocm_seg - write an on chip memory (OCM) segment
15462306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
15562306a36Sopenharmony_ci * @buffer: where to read the write data
15662306a36Sopenharmony_ci * @offs: offset into OCM to write to
15762306a36Sopenharmony_ci * @size: how many bytes to write
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * Return the number of bytes not written. Return 0 on success.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
16262306a36Sopenharmony_ci			    u32 offs, int size)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	u8 *p = buffer;
16562306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
16662306a36Sopenharmony_ci		asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
16762306a36Sopenharmony_ci	else {
16862306a36Sopenharmony_ci		for ( ; size > 0; size--, offs++, p++)
16962306a36Sopenharmony_ci			asd_write_ocm_byte(asd_ha, offs, *p);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16))
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type,
17762306a36Sopenharmony_ci			      u32 *offs, u32 *size)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int i;
18062306a36Sopenharmony_ci	struct asd_ocm_dir_ent *ent;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = 0; i < dir->num_de; i++) {
18362306a36Sopenharmony_ci		if (dir->entry[i].type == type)
18462306a36Sopenharmony_ci			break;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	if (i >= dir->num_de)
18762306a36Sopenharmony_ci		return -ENOENT;
18862306a36Sopenharmony_ci	ent = &dir->entry[i];
18962306a36Sopenharmony_ci	*offs = (u32) THREE_TO_NUM(ent->offs);
19062306a36Sopenharmony_ci	*size = (u32) THREE_TO_NUM(ent->size);
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#define OCM_BIOS_CHIM_DE  2
19562306a36Sopenharmony_ci#define BC_BIOS_PRESENT   1
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int asd_get_bios_chim(struct asd_ha_struct *asd_ha,
19862306a36Sopenharmony_ci			     struct asd_ocm_dir *dir)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int err;
20162306a36Sopenharmony_ci	struct asd_bios_chim_struct *bc_struct;
20262306a36Sopenharmony_ci	u32 offs, size;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size);
20562306a36Sopenharmony_ci	if (err) {
20662306a36Sopenharmony_ci		ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n");
20762306a36Sopenharmony_ci		goto out;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	err = -ENOMEM;
21062306a36Sopenharmony_ci	bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL);
21162306a36Sopenharmony_ci	if (!bc_struct) {
21262306a36Sopenharmony_ci		asd_printk("no memory for bios_chim struct\n");
21362306a36Sopenharmony_ci		goto out;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs,
21662306a36Sopenharmony_ci			       sizeof(*bc_struct));
21762306a36Sopenharmony_ci	if (err) {
21862306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read ocm segment\n");
21962306a36Sopenharmony_ci		goto out2;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci	if (strncmp(bc_struct->sig, "SOIB", 4)
22262306a36Sopenharmony_ci	    && strncmp(bc_struct->sig, "IPSA", 4)) {
22362306a36Sopenharmony_ci		ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n",
22462306a36Sopenharmony_ci			    bc_struct->sig[0], bc_struct->sig[1],
22562306a36Sopenharmony_ci			    bc_struct->sig[2], bc_struct->sig[3]);
22662306a36Sopenharmony_ci		err = -ENOENT;
22762306a36Sopenharmony_ci		goto out2;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	if (bc_struct->major != 1) {
23062306a36Sopenharmony_ci		asd_printk("BIOS_CHIM unsupported major version:0x%x\n",
23162306a36Sopenharmony_ci			   bc_struct->major);
23262306a36Sopenharmony_ci		err = -ENOENT;
23362306a36Sopenharmony_ci		goto out2;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	if (bc_struct->flags & BC_BIOS_PRESENT) {
23662306a36Sopenharmony_ci		asd_ha->hw_prof.bios.present = 1;
23762306a36Sopenharmony_ci		asd_ha->hw_prof.bios.maj = bc_struct->bios_major;
23862306a36Sopenharmony_ci		asd_ha->hw_prof.bios.min = bc_struct->bios_minor;
23962306a36Sopenharmony_ci		asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build);
24062306a36Sopenharmony_ci		ASD_DPRINTK("BIOS present (%d,%d), %d\n",
24162306a36Sopenharmony_ci			    asd_ha->hw_prof.bios.maj,
24262306a36Sopenharmony_ci			    asd_ha->hw_prof.bios.min,
24362306a36Sopenharmony_ci			    asd_ha->hw_prof.bios.bld);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num);
24662306a36Sopenharmony_ci	asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size);
24762306a36Sopenharmony_ci	ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num,
24862306a36Sopenharmony_ci		    asd_ha->hw_prof.ue.size);
24962306a36Sopenharmony_ci	size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size;
25062306a36Sopenharmony_ci	if (size > 0) {
25162306a36Sopenharmony_ci		err = -ENOMEM;
25262306a36Sopenharmony_ci		asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL);
25362306a36Sopenharmony_ci		if (!asd_ha->hw_prof.ue.area)
25462306a36Sopenharmony_ci			goto out2;
25562306a36Sopenharmony_ci		err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area,
25662306a36Sopenharmony_ci				       offs + sizeof(*bc_struct), size);
25762306a36Sopenharmony_ci		if (err) {
25862306a36Sopenharmony_ci			kfree(asd_ha->hw_prof.ue.area);
25962306a36Sopenharmony_ci			asd_ha->hw_prof.ue.area = NULL;
26062306a36Sopenharmony_ci			asd_ha->hw_prof.ue.num  = 0;
26162306a36Sopenharmony_ci			asd_ha->hw_prof.ue.size = 0;
26262306a36Sopenharmony_ci			ASD_DPRINTK("couldn't read ue entries(%d)\n", err);
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ciout2:
26662306a36Sopenharmony_ci	kfree(bc_struct);
26762306a36Sopenharmony_ciout:
26862306a36Sopenharmony_ci	return err;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void
27262306a36Sopenharmony_ciasd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	int i;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Zero OCM */
27762306a36Sopenharmony_ci	for (i = 0; i < OCM_MAX_SIZE; i += 4)
27862306a36Sopenharmony_ci		asd_write_ocm_dword(asd_ha, i, 0);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Write Dir */
28162306a36Sopenharmony_ci	asd_write_ocm_seg(asd_ha, &OCMDirInit, 0,
28262306a36Sopenharmony_ci			  sizeof(struct asd_ocm_dir));
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Write Dir Entries */
28562306a36Sopenharmony_ci	for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++)
28662306a36Sopenharmony_ci		asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i],
28762306a36Sopenharmony_ci				  sizeof(struct asd_ocm_dir) +
28862306a36Sopenharmony_ci				  (i * sizeof(struct asd_ocm_dir_ent))
28962306a36Sopenharmony_ci				  , sizeof(struct asd_ocm_dir_ent));
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int
29462306a36Sopenharmony_ciasd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct pci_dev *pcidev = asd_ha->pcidev;
29762306a36Sopenharmony_ci	u32 reg;
29862306a36Sopenharmony_ci	int err = 0;
29962306a36Sopenharmony_ci	u32 v;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* check if OCM has been initialized by BIOS */
30262306a36Sopenharmony_ci	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (!(reg & OCMINITIALIZED)) {
30562306a36Sopenharmony_ci		err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v);
30662306a36Sopenharmony_ci		if (err) {
30762306a36Sopenharmony_ci			asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n",
30862306a36Sopenharmony_ci					pci_name(pcidev));
30962306a36Sopenharmony_ci			goto out;
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		printk(KERN_INFO "OCM is not initialized by BIOS,"
31362306a36Sopenharmony_ci		       "reinitialize it and ignore it, current IntrptStatus"
31462306a36Sopenharmony_ci		       "is 0x%x\n", v);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (v)
31762306a36Sopenharmony_ci			err = pci_write_config_dword(pcidev,
31862306a36Sopenharmony_ci						     PCIC_INTRPT_STAT, v);
31962306a36Sopenharmony_ci		if (err) {
32062306a36Sopenharmony_ci			asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n",
32162306a36Sopenharmony_ci					pci_name(pcidev));
32262306a36Sopenharmony_ci			goto out;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		asd_hwi_initialize_ocm_dir(asd_ha);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ciout:
32962306a36Sopenharmony_ci	return err;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/**
33362306a36Sopenharmony_ci * asd_read_ocm - read on chip memory (OCM)
33462306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_ciint asd_read_ocm(struct asd_ha_struct *asd_ha)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	int err;
33962306a36Sopenharmony_ci	struct asd_ocm_dir *dir;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (asd_hwi_check_ocm_access(asd_ha))
34262306a36Sopenharmony_ci		return -1;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	dir = kmalloc(sizeof(*dir), GFP_KERNEL);
34562306a36Sopenharmony_ci	if (!dir) {
34662306a36Sopenharmony_ci		asd_printk("no memory for ocm dir\n");
34762306a36Sopenharmony_ci		return -ENOMEM;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	err = asd_read_ocm_dir(asd_ha, dir, 0);
35162306a36Sopenharmony_ci	if (err)
35262306a36Sopenharmony_ci		goto out;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	err = asd_get_bios_chim(asd_ha, dir);
35562306a36Sopenharmony_ciout:
35662306a36Sopenharmony_ci	kfree(dir);
35762306a36Sopenharmony_ci	return err;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/* ---------- FLASH stuff ---------- */
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci#define FLASH_RESET			0xF0
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci#define ASD_FLASH_SIZE                  0x200000
36562306a36Sopenharmony_ci#define FLASH_DIR_COOKIE                "*** ADAPTEC FLASH DIRECTORY *** "
36662306a36Sopenharmony_ci#define FLASH_NEXT_ENTRY_OFFS		0x2000
36762306a36Sopenharmony_ci#define FLASH_MAX_DIR_ENTRIES		32
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci#define FLASH_DE_TYPE_MASK              0x3FFFFFFF
37062306a36Sopenharmony_ci#define FLASH_DE_MS                     0x120
37162306a36Sopenharmony_ci#define FLASH_DE_CTRL_A_USER            0xE0
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistruct asd_flash_de {
37462306a36Sopenharmony_ci	__le32   type;
37562306a36Sopenharmony_ci	__le32   offs;
37662306a36Sopenharmony_ci	__le32   pad_size;
37762306a36Sopenharmony_ci	__le32   image_size;
37862306a36Sopenharmony_ci	__le32   chksum;
37962306a36Sopenharmony_ci	u8       _r[12];
38062306a36Sopenharmony_ci	u8       version[32];
38162306a36Sopenharmony_ci} __attribute__ ((packed));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistruct asd_flash_dir {
38462306a36Sopenharmony_ci	u8    cookie[32];
38562306a36Sopenharmony_ci	__le32   rev;		  /* 2 */
38662306a36Sopenharmony_ci	__le32   chksum;
38762306a36Sopenharmony_ci	__le32   chksum_antidote;
38862306a36Sopenharmony_ci	__le32   bld;
38962306a36Sopenharmony_ci	u8    bld_id[32];	  /* build id data */
39062306a36Sopenharmony_ci	u8    ver_data[32];	  /* date and time of build */
39162306a36Sopenharmony_ci	__le32   ae_mask;
39262306a36Sopenharmony_ci	__le32   v_mask;
39362306a36Sopenharmony_ci	__le32   oc_mask;
39462306a36Sopenharmony_ci	u8    _r[20];
39562306a36Sopenharmony_ci	struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES];
39662306a36Sopenharmony_ci} __attribute__ ((packed));
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistruct asd_manuf_sec {
39962306a36Sopenharmony_ci	char  sig[2];		  /* 'S', 'M' */
40062306a36Sopenharmony_ci	u16   offs_next;
40162306a36Sopenharmony_ci	u8    maj;           /* 0 */
40262306a36Sopenharmony_ci	u8    min;           /* 0 */
40362306a36Sopenharmony_ci	u16   chksum;
40462306a36Sopenharmony_ci	u16   size;
40562306a36Sopenharmony_ci	u8    _r[6];
40662306a36Sopenharmony_ci	u8    sas_addr[SAS_ADDR_SIZE];
40762306a36Sopenharmony_ci	u8    pcba_sn[ASD_PCBA_SN_SIZE];
40862306a36Sopenharmony_ci	/* Here start the other segments */
40962306a36Sopenharmony_ci	u8    linked_list[];
41062306a36Sopenharmony_ci} __attribute__ ((packed));
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistruct asd_manuf_phy_desc {
41362306a36Sopenharmony_ci	u8    state;         /* low 4 bits */
41462306a36Sopenharmony_ci#define MS_PHY_STATE_ENABLED    0
41562306a36Sopenharmony_ci#define MS_PHY_STATE_REPORTED   1
41662306a36Sopenharmony_ci#define MS_PHY_STATE_HIDDEN     2
41762306a36Sopenharmony_ci	u8    phy_id;
41862306a36Sopenharmony_ci	u16   _r;
41962306a36Sopenharmony_ci	u8    phy_control_0; /* mode 5 reg 0x160 */
42062306a36Sopenharmony_ci	u8    phy_control_1; /* mode 5 reg 0x161 */
42162306a36Sopenharmony_ci	u8    phy_control_2; /* mode 5 reg 0x162 */
42262306a36Sopenharmony_ci	u8    phy_control_3; /* mode 5 reg 0x163 */
42362306a36Sopenharmony_ci} __attribute__ ((packed));
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistruct asd_manuf_phy_param {
42662306a36Sopenharmony_ci	char  sig[2];		  /* 'P', 'M' */
42762306a36Sopenharmony_ci	u16   next;
42862306a36Sopenharmony_ci	u8    maj;           /* 0 */
42962306a36Sopenharmony_ci	u8    min;           /* 2 */
43062306a36Sopenharmony_ci	u8    num_phy_desc;  /* 8 */
43162306a36Sopenharmony_ci	u8    phy_desc_size; /* 8 */
43262306a36Sopenharmony_ci	u8    _r[3];
43362306a36Sopenharmony_ci	u8    usage_model_id;
43462306a36Sopenharmony_ci	u32   _r2;
43562306a36Sopenharmony_ci	struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS];
43662306a36Sopenharmony_ci} __attribute__ ((packed));
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci#if 0
43962306a36Sopenharmony_cistatic const char *asd_sb_type[] = {
44062306a36Sopenharmony_ci	"unknown",
44162306a36Sopenharmony_ci	"SGPIO",
44262306a36Sopenharmony_ci	[2 ... 0x7F] = "unknown",
44362306a36Sopenharmony_ci	[0x80] = "ADPT_I2C",
44462306a36Sopenharmony_ci	[0x81 ... 0xFF] = "VENDOR_UNIQUExx"
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci#endif
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistruct asd_ms_sb_desc {
44962306a36Sopenharmony_ci	u8    type;
45062306a36Sopenharmony_ci	u8    node_desc_index;
45162306a36Sopenharmony_ci	u8    conn_desc_index;
45262306a36Sopenharmony_ci	u8    _recvd[];
45362306a36Sopenharmony_ci} __attribute__ ((packed));
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci#if 0
45662306a36Sopenharmony_cistatic const char *asd_conn_type[] = {
45762306a36Sopenharmony_ci	[0 ... 7] = "unknown",
45862306a36Sopenharmony_ci	"SFF8470",
45962306a36Sopenharmony_ci	"SFF8482",
46062306a36Sopenharmony_ci	"SFF8484",
46162306a36Sopenharmony_ci	[0x80] = "PCIX_DAUGHTER0",
46262306a36Sopenharmony_ci	[0x81] = "SAS_DAUGHTER0",
46362306a36Sopenharmony_ci	[0x82 ... 0xFF] = "VENDOR_UNIQUExx"
46462306a36Sopenharmony_ci};
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic const char *asd_conn_location[] = {
46762306a36Sopenharmony_ci	"unknown",
46862306a36Sopenharmony_ci	"internal",
46962306a36Sopenharmony_ci	"external",
47062306a36Sopenharmony_ci	"board_to_board",
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci#endif
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistruct asd_ms_conn_desc {
47562306a36Sopenharmony_ci	u8    type;
47662306a36Sopenharmony_ci	u8    location;
47762306a36Sopenharmony_ci	u8    num_sideband_desc;
47862306a36Sopenharmony_ci	u8    size_sideband_desc;
47962306a36Sopenharmony_ci	u32   _resvd;
48062306a36Sopenharmony_ci	u8    name[16];
48162306a36Sopenharmony_ci	struct asd_ms_sb_desc sb_desc[];
48262306a36Sopenharmony_ci} __attribute__ ((packed));
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistruct asd_nd_phy_desc {
48562306a36Sopenharmony_ci	u8    vp_attch_type;
48662306a36Sopenharmony_ci	u8    attch_specific[];
48762306a36Sopenharmony_ci} __attribute__ ((packed));
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci#if 0
49062306a36Sopenharmony_cistatic const char *asd_node_type[] = {
49162306a36Sopenharmony_ci	"IOP",
49262306a36Sopenharmony_ci	"IO_CONTROLLER",
49362306a36Sopenharmony_ci	"EXPANDER",
49462306a36Sopenharmony_ci	"PORT_MULTIPLIER",
49562306a36Sopenharmony_ci	"PORT_MULTIPLEXER",
49662306a36Sopenharmony_ci	"MULTI_DROP_I2C_BUS",
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci#endif
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistruct asd_ms_node_desc {
50162306a36Sopenharmony_ci	u8    type;
50262306a36Sopenharmony_ci	u8    num_phy_desc;
50362306a36Sopenharmony_ci	u8    size_phy_desc;
50462306a36Sopenharmony_ci	u8    _resvd;
50562306a36Sopenharmony_ci	u8    name[16];
50662306a36Sopenharmony_ci	struct asd_nd_phy_desc phy_desc[];
50762306a36Sopenharmony_ci} __attribute__ ((packed));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistruct asd_ms_conn_map {
51062306a36Sopenharmony_ci	char  sig[2];		  /* 'M', 'C' */
51162306a36Sopenharmony_ci	__le16 next;
51262306a36Sopenharmony_ci	u8    maj;		  /* 0 */
51362306a36Sopenharmony_ci	u8    min;		  /* 0 */
51462306a36Sopenharmony_ci	__le16 cm_size;		  /* size of this struct */
51562306a36Sopenharmony_ci	u8    num_conn;
51662306a36Sopenharmony_ci	u8    conn_size;
51762306a36Sopenharmony_ci	u8    num_nodes;
51862306a36Sopenharmony_ci	u8    usage_model_id;
51962306a36Sopenharmony_ci	u32   _resvd;
52062306a36Sopenharmony_ci	union {
52162306a36Sopenharmony_ci		DECLARE_FLEX_ARRAY(struct asd_ms_conn_desc, conn_desc);
52262306a36Sopenharmony_ci		DECLARE_FLEX_ARRAY(struct asd_ms_node_desc, node_desc);
52362306a36Sopenharmony_ci	};
52462306a36Sopenharmony_ci} __attribute__ ((packed));
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistruct asd_ctrla_phy_entry {
52762306a36Sopenharmony_ci	u8    sas_addr[SAS_ADDR_SIZE];
52862306a36Sopenharmony_ci	u8    sas_link_rates;  /* max in hi bits, min in low bits */
52962306a36Sopenharmony_ci	u8    flags;
53062306a36Sopenharmony_ci	u8    sata_link_rates;
53162306a36Sopenharmony_ci	u8    _r[5];
53262306a36Sopenharmony_ci} __attribute__ ((packed));
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistruct asd_ctrla_phy_settings {
53562306a36Sopenharmony_ci	u8    id0;		  /* P'h'y */
53662306a36Sopenharmony_ci	u8    _r;
53762306a36Sopenharmony_ci	u16   next;
53862306a36Sopenharmony_ci	u8    num_phys;	      /* number of PHYs in the PCI function */
53962306a36Sopenharmony_ci	u8    _r2[3];
54062306a36Sopenharmony_ci	struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS];
54162306a36Sopenharmony_ci} __attribute__ ((packed));
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistruct asd_ll_el {
54462306a36Sopenharmony_ci	u8   id0;
54562306a36Sopenharmony_ci	u8   id1;
54662306a36Sopenharmony_ci	__le16  next;
54762306a36Sopenharmony_ci	u8   something_here[];
54862306a36Sopenharmony_ci} __attribute__ ((packed));
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int asd_poll_flash(struct asd_ha_struct *asd_ha)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	int c;
55362306a36Sopenharmony_ci	u8 d;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	for (c = 5000; c > 0; c--) {
55662306a36Sopenharmony_ci		d  = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
55762306a36Sopenharmony_ci		d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
55862306a36Sopenharmony_ci		if (!d)
55962306a36Sopenharmony_ci			return 0;
56062306a36Sopenharmony_ci		udelay(5);
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	return -ENOENT;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int asd_reset_flash(struct asd_ha_struct *asd_ha)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	int err;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	err = asd_poll_flash(asd_ha);
57062306a36Sopenharmony_ci	if (err)
57162306a36Sopenharmony_ci		return err;
57262306a36Sopenharmony_ci	asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET);
57362306a36Sopenharmony_ci	err = asd_poll_flash(asd_ha);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return err;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
57962306a36Sopenharmony_ci			      void *buffer, u32 offs, int size)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
58262306a36Sopenharmony_ci			    size);
58362306a36Sopenharmony_ci	return 0;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/**
58762306a36Sopenharmony_ci * asd_find_flash_dir - finds and reads the flash directory
58862306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
58962306a36Sopenharmony_ci * @flash_dir: pointer to flash directory structure
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci * If found, the flash directory segment will be copied to
59262306a36Sopenharmony_ci * @flash_dir.  Return 1 if found, 0 if not.
59362306a36Sopenharmony_ci */
59462306a36Sopenharmony_cistatic int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
59562306a36Sopenharmony_ci			      struct asd_flash_dir *flash_dir)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	u32 v;
59862306a36Sopenharmony_ci	for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
59962306a36Sopenharmony_ci		asd_read_flash_seg(asd_ha, flash_dir, v,
60062306a36Sopenharmony_ci				   sizeof(FLASH_DIR_COOKIE)-1);
60162306a36Sopenharmony_ci		if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
60262306a36Sopenharmony_ci			   sizeof(FLASH_DIR_COOKIE)-1) == 0) {
60362306a36Sopenharmony_ci			asd_ha->hw_prof.flash.dir_offs = v;
60462306a36Sopenharmony_ci			asd_read_flash_seg(asd_ha, flash_dir, v,
60562306a36Sopenharmony_ci					   sizeof(*flash_dir));
60662306a36Sopenharmony_ci			return 1;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int asd_flash_getid(struct asd_ha_struct *asd_ha)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	int err = 0;
61562306a36Sopenharmony_ci	u32 reg;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
62062306a36Sopenharmony_ci				  &asd_ha->hw_prof.flash.bar)) {
62162306a36Sopenharmony_ci		asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
62262306a36Sopenharmony_ci			   pci_name(asd_ha->pcidev));
62362306a36Sopenharmony_ci		return -ENOENT;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	asd_ha->hw_prof.flash.present = 1;
62662306a36Sopenharmony_ci	asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0;
62762306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
62862306a36Sopenharmony_ci	if (err) {
62962306a36Sopenharmony_ci		ASD_DPRINTK("couldn't reset flash(%d)\n", err);
63062306a36Sopenharmony_ci		return err;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic u16 asd_calc_flash_chksum(u16 *p, int size)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	u16 chksum = 0;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	while (size-- > 0)
64062306a36Sopenharmony_ci		chksum += *p++;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return chksum;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type,
64762306a36Sopenharmony_ci			     u32 *offs, u32 *size)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int i;
65062306a36Sopenharmony_ci	struct asd_flash_de *de;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) {
65362306a36Sopenharmony_ci		u32 type = le32_to_cpu(flash_dir->dir_entry[i].type);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		type &= FLASH_DE_TYPE_MASK;
65662306a36Sopenharmony_ci		if (type == entry_type)
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	if (i >= FLASH_MAX_DIR_ENTRIES)
66062306a36Sopenharmony_ci		return -ENOENT;
66162306a36Sopenharmony_ci	de = &flash_dir->dir_entry[i];
66262306a36Sopenharmony_ci	*offs = le32_to_cpu(de->offs);
66362306a36Sopenharmony_ci	*size = le32_to_cpu(de->pad_size);
66462306a36Sopenharmony_ci	return 0;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic int asd_validate_ms(struct asd_manuf_sec *ms)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	if (ms->sig[0] != 'S' || ms->sig[1] != 'M') {
67062306a36Sopenharmony_ci		ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n",
67162306a36Sopenharmony_ci			    ms->sig[0], ms->sig[1]);
67262306a36Sopenharmony_ci		return -ENOENT;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci	if (ms->maj != 0) {
67562306a36Sopenharmony_ci		asd_printk("unsupported manuf. sector. major version:%x\n",
67662306a36Sopenharmony_ci			   ms->maj);
67762306a36Sopenharmony_ci		return -ENOENT;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next);
68062306a36Sopenharmony_ci	ms->chksum = le16_to_cpu((__force __le16) ms->chksum);
68162306a36Sopenharmony_ci	ms->size = le16_to_cpu((__force __le16) ms->size);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) {
68462306a36Sopenharmony_ci		asd_printk("failed manuf sector checksum\n");
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	return 0;
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha,
69162306a36Sopenharmony_ci			       struct asd_manuf_sec *ms)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE);
69462306a36Sopenharmony_ci	return 0;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha,
69862306a36Sopenharmony_ci			      struct asd_manuf_sec *ms)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE);
70162306a36Sopenharmony_ci	asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0';
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/**
70662306a36Sopenharmony_ci * asd_find_ll_by_id - find a linked list entry by its id
70762306a36Sopenharmony_ci * @start: void pointer to the first element in the linked list
70862306a36Sopenharmony_ci * @id0: the first byte of the id  (offs 0)
70962306a36Sopenharmony_ci * @id1: the second byte of the id (offs 1)
71062306a36Sopenharmony_ci *
71162306a36Sopenharmony_ci * @start has to be the _base_ element start, since the
71262306a36Sopenharmony_ci * linked list entries's offset is from this pointer.
71362306a36Sopenharmony_ci * Some linked list entries use only the first id, in which case
71462306a36Sopenharmony_ci * you can pass 0xFF for the second.
71562306a36Sopenharmony_ci */
71662306a36Sopenharmony_cistatic void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct asd_ll_el *el = start;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	do {
72162306a36Sopenharmony_ci		switch (id1) {
72262306a36Sopenharmony_ci		default:
72362306a36Sopenharmony_ci			if (el->id1 == id1) {
72462306a36Sopenharmony_ci			fallthrough;
72562306a36Sopenharmony_ci		case 0xFF:
72662306a36Sopenharmony_ci				if (el->id0 == id0)
72762306a36Sopenharmony_ci					return el;
72862306a36Sopenharmony_ci			}
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci		el = start + le16_to_cpu(el->next);
73162306a36Sopenharmony_ci	} while (el != start);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return NULL;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/**
73762306a36Sopenharmony_ci * asd_ms_get_phy_params - get phy parameters from the manufacturing sector
73862306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
73962306a36Sopenharmony_ci * @manuf_sec: pointer to the manufacturing sector
74062306a36Sopenharmony_ci *
74162306a36Sopenharmony_ci * The manufacturing sector contans also the linked list of sub-segments,
74262306a36Sopenharmony_ci * since when it was read, its size was taken from the flash directory,
74362306a36Sopenharmony_ci * not from the structure size.
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * HIDDEN phys do not count in the total count.  REPORTED phys cannot
74662306a36Sopenharmony_ci * be enabled but are reported and counted towards the total.
74762306a36Sopenharmony_ci * ENABLED phys are enabled by default and count towards the total.
74862306a36Sopenharmony_ci * The absolute total phy number is ASD_MAX_PHYS.  hw_prof->num_phys
74962306a36Sopenharmony_ci * merely specifies the number of phys the host adapter decided to
75062306a36Sopenharmony_ci * report.  E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
75162306a36Sopenharmony_ci * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED.
75262306a36Sopenharmony_ci * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
75362306a36Sopenharmony_ci * are actually enabled (enabled by default, max number of phys
75462306a36Sopenharmony_ci * enableable in this case).
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_cistatic int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
75762306a36Sopenharmony_ci				 struct asd_manuf_sec *manuf_sec)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int i;
76062306a36Sopenharmony_ci	int en_phys = 0;
76162306a36Sopenharmony_ci	int rep_phys = 0;
76262306a36Sopenharmony_ci	struct asd_manuf_phy_param *phy_param;
76362306a36Sopenharmony_ci	struct asd_manuf_phy_param dflt_phy_param;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M');
76662306a36Sopenharmony_ci	if (!phy_param) {
76762306a36Sopenharmony_ci		ASD_DPRINTK("ms: no phy parameters found\n");
76862306a36Sopenharmony_ci		ASD_DPRINTK("ms: Creating default phy parameters\n");
76962306a36Sopenharmony_ci		dflt_phy_param.sig[0] = 'P';
77062306a36Sopenharmony_ci		dflt_phy_param.sig[1] = 'M';
77162306a36Sopenharmony_ci		dflt_phy_param.maj = 0;
77262306a36Sopenharmony_ci		dflt_phy_param.min = 2;
77362306a36Sopenharmony_ci		dflt_phy_param.num_phy_desc = 8;
77462306a36Sopenharmony_ci		dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc);
77562306a36Sopenharmony_ci		for (i =0; i < ASD_MAX_PHYS; i++) {
77662306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].state = 0;
77762306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].phy_id = i;
77862306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6;
77962306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].phy_control_1 = 0x10;
78062306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].phy_control_2 = 0x43;
78162306a36Sopenharmony_ci			dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		phy_param = &dflt_phy_param;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (phy_param->maj != 0) {
78962306a36Sopenharmony_ci		asd_printk("unsupported manuf. phy param major version:0x%x\n",
79062306a36Sopenharmony_ci			   phy_param->maj);
79162306a36Sopenharmony_ci		return -ENOENT;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc);
79562306a36Sopenharmony_ci	asd_ha->hw_prof.enabled_phys = 0;
79662306a36Sopenharmony_ci	for (i = 0; i < phy_param->num_phy_desc; i++) {
79762306a36Sopenharmony_ci		struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i];
79862306a36Sopenharmony_ci		switch (pd->state & 0xF) {
79962306a36Sopenharmony_ci		case MS_PHY_STATE_HIDDEN:
80062306a36Sopenharmony_ci			ASD_DPRINTK("ms: phy%d: HIDDEN\n", i);
80162306a36Sopenharmony_ci			continue;
80262306a36Sopenharmony_ci		case MS_PHY_STATE_REPORTED:
80362306a36Sopenharmony_ci			ASD_DPRINTK("ms: phy%d: REPORTED\n", i);
80462306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
80562306a36Sopenharmony_ci			rep_phys++;
80662306a36Sopenharmony_ci			continue;
80762306a36Sopenharmony_ci		case MS_PHY_STATE_ENABLED:
80862306a36Sopenharmony_ci			ASD_DPRINTK("ms: phy%d: ENABLED\n", i);
80962306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys |= (1 << i);
81062306a36Sopenharmony_ci			en_phys++;
81162306a36Sopenharmony_ci			break;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0;
81462306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1;
81562306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2;
81662306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	asd_ha->hw_prof.max_phys = rep_phys + en_phys;
81962306a36Sopenharmony_ci	asd_ha->hw_prof.num_phys = en_phys;
82062306a36Sopenharmony_ci	ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n",
82162306a36Sopenharmony_ci		    asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys);
82262306a36Sopenharmony_ci	ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys);
82362306a36Sopenharmony_ci	return 0;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha,
82762306a36Sopenharmony_ci				    struct asd_manuf_sec *manuf_sec)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	struct asd_ms_conn_map *cm;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	cm = asd_find_ll_by_id(manuf_sec, 'M', 'C');
83262306a36Sopenharmony_ci	if (!cm) {
83362306a36Sopenharmony_ci		ASD_DPRINTK("ms: no connector map found\n");
83462306a36Sopenharmony_ci		return 0;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (cm->maj != 0) {
83862306a36Sopenharmony_ci		ASD_DPRINTK("ms: unsupported: connector map major version 0x%x"
83962306a36Sopenharmony_ci			    "\n", cm->maj);
84062306a36Sopenharmony_ci		return -ENOENT;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* XXX */
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return 0;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/**
85062306a36Sopenharmony_ci * asd_process_ms - find and extract information from the manufacturing sector
85162306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
85262306a36Sopenharmony_ci * @flash_dir: pointer to the flash directory
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic int asd_process_ms(struct asd_ha_struct *asd_ha,
85562306a36Sopenharmony_ci			  struct asd_flash_dir *flash_dir)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	int err;
85862306a36Sopenharmony_ci	struct asd_manuf_sec *manuf_sec;
85962306a36Sopenharmony_ci	u32 offs, size;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size);
86262306a36Sopenharmony_ci	if (err) {
86362306a36Sopenharmony_ci		ASD_DPRINTK("Couldn't find the manuf. sector\n");
86462306a36Sopenharmony_ci		goto out;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (size == 0)
86862306a36Sopenharmony_ci		goto out;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	err = -ENOMEM;
87162306a36Sopenharmony_ci	manuf_sec = kmalloc(size, GFP_KERNEL);
87262306a36Sopenharmony_ci	if (!manuf_sec) {
87362306a36Sopenharmony_ci		ASD_DPRINTK("no mem for manuf sector\n");
87462306a36Sopenharmony_ci		goto out;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size);
87862306a36Sopenharmony_ci	if (err) {
87962306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n",
88062306a36Sopenharmony_ci			    offs, size);
88162306a36Sopenharmony_ci		goto out2;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	err = asd_validate_ms(manuf_sec);
88562306a36Sopenharmony_ci	if (err) {
88662306a36Sopenharmony_ci		ASD_DPRINTK("couldn't validate manuf sector\n");
88762306a36Sopenharmony_ci		goto out2;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	err = asd_ms_get_sas_addr(asd_ha, manuf_sec);
89162306a36Sopenharmony_ci	if (err) {
89262306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read the SAS_ADDR\n");
89362306a36Sopenharmony_ci		goto out2;
89462306a36Sopenharmony_ci	}
89562306a36Sopenharmony_ci	ASD_DPRINTK("manuf sect SAS_ADDR %llx\n",
89662306a36Sopenharmony_ci		    SAS_ADDR(asd_ha->hw_prof.sas_addr));
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	err = asd_ms_get_pcba_sn(asd_ha, manuf_sec);
89962306a36Sopenharmony_ci	if (err) {
90062306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read the PCBA SN\n");
90162306a36Sopenharmony_ci		goto out2;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci	ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	err = asd_ms_get_phy_params(asd_ha, manuf_sec);
90662306a36Sopenharmony_ci	if (err) {
90762306a36Sopenharmony_ci		ASD_DPRINTK("ms: couldn't get phy parameters\n");
90862306a36Sopenharmony_ci		goto out2;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	err = asd_ms_get_connector_map(asd_ha, manuf_sec);
91262306a36Sopenharmony_ci	if (err) {
91362306a36Sopenharmony_ci		ASD_DPRINTK("ms: couldn't get connector map\n");
91462306a36Sopenharmony_ci		goto out2;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ciout2:
91862306a36Sopenharmony_ci	kfree(manuf_sec);
91962306a36Sopenharmony_ciout:
92062306a36Sopenharmony_ci	return err;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha,
92462306a36Sopenharmony_ci					  struct asd_ctrla_phy_settings *ps)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	int i;
92762306a36Sopenharmony_ci	for (i = 0; i < ps->num_phys; i++) {
92862306a36Sopenharmony_ci		struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i];
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		if (!PHY_ENABLED(asd_ha, i))
93162306a36Sopenharmony_ci			continue;
93262306a36Sopenharmony_ci		if (*(u64 *)pe->sas_addr == 0) {
93362306a36Sopenharmony_ci			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
93462306a36Sopenharmony_ci			continue;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci		/* This is the SAS address which should be sent in IDENTIFY. */
93762306a36Sopenharmony_ci		memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr,
93862306a36Sopenharmony_ci		       SAS_ADDR_SIZE);
93962306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].max_sas_lrate =
94062306a36Sopenharmony_ci			(pe->sas_link_rates & 0xF0) >> 4;
94162306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].min_sas_lrate =
94262306a36Sopenharmony_ci			(pe->sas_link_rates & 0x0F);
94362306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].max_sata_lrate =
94462306a36Sopenharmony_ci			(pe->sata_link_rates & 0xF0) >> 4;
94562306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].min_sata_lrate =
94662306a36Sopenharmony_ci			(pe->sata_link_rates & 0x0F);
94762306a36Sopenharmony_ci		asd_ha->hw_prof.phy_desc[i].flags = pe->flags;
94862306a36Sopenharmony_ci		ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x,"
94962306a36Sopenharmony_ci			    " sata rate:0x%x-0x%x, flags:0x%x\n",
95062306a36Sopenharmony_ci			    i,
95162306a36Sopenharmony_ci			    SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr),
95262306a36Sopenharmony_ci			    asd_ha->hw_prof.phy_desc[i].max_sas_lrate,
95362306a36Sopenharmony_ci			    asd_ha->hw_prof.phy_desc[i].min_sas_lrate,
95462306a36Sopenharmony_ci			    asd_ha->hw_prof.phy_desc[i].max_sata_lrate,
95562306a36Sopenharmony_ci			    asd_ha->hw_prof.phy_desc[i].min_sata_lrate,
95662306a36Sopenharmony_ci			    asd_ha->hw_prof.phy_desc[i].flags);
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/**
96362306a36Sopenharmony_ci * asd_process_ctrl_a_user - process CTRL-A user settings
96462306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
96562306a36Sopenharmony_ci * @flash_dir: pointer to the flash directory
96662306a36Sopenharmony_ci */
96762306a36Sopenharmony_cistatic int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
96862306a36Sopenharmony_ci				   struct asd_flash_dir *flash_dir)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	int err, i;
97162306a36Sopenharmony_ci	u32 offs, size;
97262306a36Sopenharmony_ci	struct asd_ll_el *el = NULL;
97362306a36Sopenharmony_ci	struct asd_ctrla_phy_settings *ps;
97462306a36Sopenharmony_ci	struct asd_ctrla_phy_settings dflt_ps;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size);
97762306a36Sopenharmony_ci	if (err) {
97862306a36Sopenharmony_ci		ASD_DPRINTK("couldn't find CTRL-A user settings section\n");
97962306a36Sopenharmony_ci		ASD_DPRINTK("Creating default CTRL-A user settings section\n");
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		dflt_ps.id0 = 'h';
98262306a36Sopenharmony_ci		dflt_ps.num_phys = 8;
98362306a36Sopenharmony_ci		for (i =0; i < ASD_MAX_PHYS; i++) {
98462306a36Sopenharmony_ci			memcpy(dflt_ps.phy_ent[i].sas_addr,
98562306a36Sopenharmony_ci			       asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
98662306a36Sopenharmony_ci			dflt_ps.phy_ent[i].sas_link_rates = 0x98;
98762306a36Sopenharmony_ci			dflt_ps.phy_ent[i].flags = 0x0;
98862306a36Sopenharmony_ci			dflt_ps.phy_ent[i].sata_link_rates = 0x0;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		size = sizeof(struct asd_ctrla_phy_settings);
99262306a36Sopenharmony_ci		ps = &dflt_ps;
99362306a36Sopenharmony_ci		goto out_process;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (size == 0)
99762306a36Sopenharmony_ci		goto out;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	err = -ENOMEM;
100062306a36Sopenharmony_ci	el = kmalloc(size, GFP_KERNEL);
100162306a36Sopenharmony_ci	if (!el) {
100262306a36Sopenharmony_ci		ASD_DPRINTK("no mem for ctrla user settings section\n");
100362306a36Sopenharmony_ci		goto out;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	err = asd_read_flash_seg(asd_ha, (void *)el, offs, size);
100762306a36Sopenharmony_ci	if (err) {
100862306a36Sopenharmony_ci		ASD_DPRINTK("couldn't read ctrla phy settings section\n");
100962306a36Sopenharmony_ci		goto out2;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	err = -ENOENT;
101362306a36Sopenharmony_ci	ps = asd_find_ll_by_id(el, 'h', 0xFF);
101462306a36Sopenharmony_ci	if (!ps) {
101562306a36Sopenharmony_ci		ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
101662306a36Sopenharmony_ci		goto out2;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ciout_process:
101962306a36Sopenharmony_ci	err = asd_process_ctrla_phy_settings(asd_ha, ps);
102062306a36Sopenharmony_ci	if (err) {
102162306a36Sopenharmony_ci		ASD_DPRINTK("couldn't process ctrla phy settings\n");
102262306a36Sopenharmony_ci		goto out2;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ciout2:
102562306a36Sopenharmony_ci	kfree(el);
102662306a36Sopenharmony_ciout:
102762306a36Sopenharmony_ci	return err;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci/**
103162306a36Sopenharmony_ci * asd_read_flash - read flash memory
103262306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
103362306a36Sopenharmony_ci */
103462306a36Sopenharmony_ciint asd_read_flash(struct asd_ha_struct *asd_ha)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	int err;
103762306a36Sopenharmony_ci	struct asd_flash_dir *flash_dir;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	err = asd_flash_getid(asd_ha);
104062306a36Sopenharmony_ci	if (err)
104162306a36Sopenharmony_ci		return err;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL);
104462306a36Sopenharmony_ci	if (!flash_dir)
104562306a36Sopenharmony_ci		return -ENOMEM;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	err = -ENOENT;
104862306a36Sopenharmony_ci	if (!asd_find_flash_dir(asd_ha, flash_dir)) {
104962306a36Sopenharmony_ci		ASD_DPRINTK("couldn't find flash directory\n");
105062306a36Sopenharmony_ci		goto out;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (le32_to_cpu(flash_dir->rev) != 2) {
105462306a36Sopenharmony_ci		asd_printk("unsupported flash dir version:0x%x\n",
105562306a36Sopenharmony_ci			   le32_to_cpu(flash_dir->rev));
105662306a36Sopenharmony_ci		goto out;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	err = asd_process_ms(asd_ha, flash_dir);
106062306a36Sopenharmony_ci	if (err) {
106162306a36Sopenharmony_ci		ASD_DPRINTK("couldn't process manuf sector settings\n");
106262306a36Sopenharmony_ci		goto out;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	err = asd_process_ctrl_a_user(asd_ha, flash_dir);
106662306a36Sopenharmony_ci	if (err) {
106762306a36Sopenharmony_ci		ASD_DPRINTK("couldn't process CTRL-A user settings\n");
106862306a36Sopenharmony_ci		goto out;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciout:
107262306a36Sopenharmony_ci	kfree(flash_dir);
107362306a36Sopenharmony_ci	return err;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci/**
107762306a36Sopenharmony_ci * asd_verify_flash_seg - verify data with flash memory
107862306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
107962306a36Sopenharmony_ci * @src: pointer to the source data to be verified
108062306a36Sopenharmony_ci * @dest_offset: offset from flash memory
108162306a36Sopenharmony_ci * @bytes_to_verify: total bytes to verify
108262306a36Sopenharmony_ci */
108362306a36Sopenharmony_ciint asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
108462306a36Sopenharmony_ci			 const void *src, u32 dest_offset, u32 bytes_to_verify)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	const u8 *src_buf;
108762306a36Sopenharmony_ci	u8 flash_char;
108862306a36Sopenharmony_ci	int err;
108962306a36Sopenharmony_ci	u32 nv_offset, reg, i;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	reg = asd_ha->hw_prof.flash.bar;
109262306a36Sopenharmony_ci	src_buf = NULL;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	err = FLASH_OK;
109562306a36Sopenharmony_ci	nv_offset = dest_offset;
109662306a36Sopenharmony_ci	src_buf = (const u8 *)src;
109762306a36Sopenharmony_ci	for (i = 0; i < bytes_to_verify; i++) {
109862306a36Sopenharmony_ci		flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i);
109962306a36Sopenharmony_ci		if (flash_char != src_buf[i]) {
110062306a36Sopenharmony_ci			err = FAIL_VERIFY;
110162306a36Sopenharmony_ci			break;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci	return err;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci/**
110862306a36Sopenharmony_ci * asd_write_flash_seg - write data into flash memory
110962306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
111062306a36Sopenharmony_ci * @src: pointer to the source data to be written
111162306a36Sopenharmony_ci * @dest_offset: offset from flash memory
111262306a36Sopenharmony_ci * @bytes_to_write: total bytes to write
111362306a36Sopenharmony_ci */
111462306a36Sopenharmony_ciint asd_write_flash_seg(struct asd_ha_struct *asd_ha,
111562306a36Sopenharmony_ci			const void *src, u32 dest_offset, u32 bytes_to_write)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	const u8 *src_buf;
111862306a36Sopenharmony_ci	u32 nv_offset, reg, i;
111962306a36Sopenharmony_ci	int err;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	reg = asd_ha->hw_prof.flash.bar;
112262306a36Sopenharmony_ci	src_buf = NULL;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	err = asd_check_flash_type(asd_ha);
112562306a36Sopenharmony_ci	if (err) {
112662306a36Sopenharmony_ci		ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err);
112762306a36Sopenharmony_ci		return err;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	nv_offset = dest_offset;
113162306a36Sopenharmony_ci	err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write);
113262306a36Sopenharmony_ci	if (err) {
113362306a36Sopenharmony_ci		ASD_DPRINTK("Erase failed at offset:0x%x\n",
113462306a36Sopenharmony_ci			nv_offset);
113562306a36Sopenharmony_ci		return err;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
113962306a36Sopenharmony_ci	if (err) {
114062306a36Sopenharmony_ci		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
114162306a36Sopenharmony_ci		return err;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	src_buf = (const u8 *)src;
114562306a36Sopenharmony_ci	for (i = 0; i < bytes_to_write; i++) {
114662306a36Sopenharmony_ci		/* Setup program command sequence */
114762306a36Sopenharmony_ci		switch (asd_ha->hw_prof.flash.method) {
114862306a36Sopenharmony_ci		case FLASH_METHOD_A:
114962306a36Sopenharmony_ci		{
115062306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
115162306a36Sopenharmony_ci					(reg + 0xAAA), 0xAA);
115262306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
115362306a36Sopenharmony_ci					(reg + 0x555), 0x55);
115462306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
115562306a36Sopenharmony_ci					(reg + 0xAAA), 0xA0);
115662306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
115762306a36Sopenharmony_ci					(reg + nv_offset + i),
115862306a36Sopenharmony_ci					(*(src_buf + i)));
115962306a36Sopenharmony_ci			break;
116062306a36Sopenharmony_ci		}
116162306a36Sopenharmony_ci		case FLASH_METHOD_B:
116262306a36Sopenharmony_ci		{
116362306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
116462306a36Sopenharmony_ci					(reg + 0x555), 0xAA);
116562306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
116662306a36Sopenharmony_ci					(reg + 0x2AA), 0x55);
116762306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
116862306a36Sopenharmony_ci					(reg + 0x555), 0xA0);
116962306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha,
117062306a36Sopenharmony_ci					(reg + nv_offset + i),
117162306a36Sopenharmony_ci					(*(src_buf + i)));
117262306a36Sopenharmony_ci			break;
117362306a36Sopenharmony_ci		}
117462306a36Sopenharmony_ci		default:
117562306a36Sopenharmony_ci			break;
117662306a36Sopenharmony_ci		}
117762306a36Sopenharmony_ci		if (asd_chk_write_status(asd_ha,
117862306a36Sopenharmony_ci				(nv_offset + i), 0) != 0) {
117962306a36Sopenharmony_ci			ASD_DPRINTK("aicx: Write failed at offset:0x%x\n",
118062306a36Sopenharmony_ci				reg + nv_offset + i);
118162306a36Sopenharmony_ci			return FAIL_WRITE_FLASH;
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
118662306a36Sopenharmony_ci	if (err) {
118762306a36Sopenharmony_ci		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
118862306a36Sopenharmony_ci		return err;
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci	return 0;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ciint asd_chk_write_status(struct asd_ha_struct *asd_ha,
119462306a36Sopenharmony_ci	 u32 sector_addr, u8 erase_flag)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	u32 reg;
119762306a36Sopenharmony_ci	u32 loop_cnt;
119862306a36Sopenharmony_ci	u8  nv_data1, nv_data2;
119962306a36Sopenharmony_ci	u8  toggle_bit1;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/*
120262306a36Sopenharmony_ci	 * Read from DQ2 requires sector address
120362306a36Sopenharmony_ci	 * while it's dont care for DQ6
120462306a36Sopenharmony_ci	 */
120562306a36Sopenharmony_ci	reg = asd_ha->hw_prof.flash.bar;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) {
120862306a36Sopenharmony_ci		nv_data1 = asd_read_reg_byte(asd_ha, reg);
120962306a36Sopenharmony_ci		nv_data2 = asd_read_reg_byte(asd_ha, reg);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
121262306a36Sopenharmony_ci				 ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		if (toggle_bit1 == 0) {
121562306a36Sopenharmony_ci			return 0;
121662306a36Sopenharmony_ci		} else {
121762306a36Sopenharmony_ci			if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) {
121862306a36Sopenharmony_ci				nv_data1 = asd_read_reg_byte(asd_ha,
121962306a36Sopenharmony_ci								reg);
122062306a36Sopenharmony_ci				nv_data2 = asd_read_reg_byte(asd_ha,
122162306a36Sopenharmony_ci								reg);
122262306a36Sopenharmony_ci				toggle_bit1 =
122362306a36Sopenharmony_ci				((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
122462306a36Sopenharmony_ci				^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci				if (toggle_bit1 == 0)
122762306a36Sopenharmony_ci					return 0;
122862306a36Sopenharmony_ci			}
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		/*
123262306a36Sopenharmony_ci		 * ERASE is a sector-by-sector operation and requires
123362306a36Sopenharmony_ci		 * more time to finish while WRITE is byte-byte-byte
123462306a36Sopenharmony_ci		 * operation and takes lesser time to finish.
123562306a36Sopenharmony_ci		 *
123662306a36Sopenharmony_ci		 * For some strange reason a reduced ERASE delay gives different
123762306a36Sopenharmony_ci		 * behaviour across different spirit boards. Hence we set
123862306a36Sopenharmony_ci		 * a optimum balance of 50mus for ERASE which works well
123962306a36Sopenharmony_ci		 * across all boards.
124062306a36Sopenharmony_ci		 */
124162306a36Sopenharmony_ci		if (erase_flag) {
124262306a36Sopenharmony_ci			udelay(FLASH_STATUS_ERASE_DELAY_COUNT);
124362306a36Sopenharmony_ci		} else {
124462306a36Sopenharmony_ci			udelay(FLASH_STATUS_WRITE_DELAY_COUNT);
124562306a36Sopenharmony_ci		}
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci	return -1;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/**
125162306a36Sopenharmony_ci * asd_erase_nv_sector - Erase the flash memory sectors.
125262306a36Sopenharmony_ci * @asd_ha: pointer to the host adapter structure
125362306a36Sopenharmony_ci * @flash_addr: pointer to offset from flash memory
125462306a36Sopenharmony_ci * @size: total bytes to erase.
125562306a36Sopenharmony_ci */
125662306a36Sopenharmony_ciint asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	u32 reg;
125962306a36Sopenharmony_ci	u32 sector_addr;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	reg = asd_ha->hw_prof.flash.bar;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/* sector staring address */
126462306a36Sopenharmony_ci	sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	/*
126762306a36Sopenharmony_ci	 * Erasing an flash sector needs to be done in six consecutive
126862306a36Sopenharmony_ci	 * write cyles.
126962306a36Sopenharmony_ci	 */
127062306a36Sopenharmony_ci	while (sector_addr < flash_addr+size) {
127162306a36Sopenharmony_ci		switch (asd_ha->hw_prof.flash.method) {
127262306a36Sopenharmony_ci		case FLASH_METHOD_A:
127362306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
127462306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
127562306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80);
127662306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
127762306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
127862306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
127962306a36Sopenharmony_ci			break;
128062306a36Sopenharmony_ci		case FLASH_METHOD_B:
128162306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
128262306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
128362306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80);
128462306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
128562306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
128662306a36Sopenharmony_ci			asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
128762306a36Sopenharmony_ci			break;
128862306a36Sopenharmony_ci		default:
128962306a36Sopenharmony_ci			break;
129062306a36Sopenharmony_ci		}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0)
129362306a36Sopenharmony_ci			return FAIL_ERASE_FLASH;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci		sector_addr += FLASH_SECTOR_SIZE;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return 0;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ciint asd_check_flash_type(struct asd_ha_struct *asd_ha)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	u8 manuf_id;
130462306a36Sopenharmony_ci	u8 dev_id;
130562306a36Sopenharmony_ci	u8 sec_prot;
130662306a36Sopenharmony_ci	u32 inc;
130762306a36Sopenharmony_ci	u32 reg;
130862306a36Sopenharmony_ci	int err;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* get Flash memory base address */
131162306a36Sopenharmony_ci	reg = asd_ha->hw_prof.flash.bar;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* Determine flash info */
131462306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
131562306a36Sopenharmony_ci	if (err) {
131662306a36Sopenharmony_ci		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
131762306a36Sopenharmony_ci		return err;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN;
132162306a36Sopenharmony_ci	asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN;
132262306a36Sopenharmony_ci	asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* Get flash info. This would most likely be AMD Am29LV family flash.
132562306a36Sopenharmony_ci	 * First try the sequence for word mode.  It is the same as for
132662306a36Sopenharmony_ci	 * 008B (byte mode only), 160B (word mode) and 800D (word mode).
132762306a36Sopenharmony_ci	 */
132862306a36Sopenharmony_ci	inc = asd_ha->hw_prof.flash.wide ? 2 : 1;
132962306a36Sopenharmony_ci	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA);
133062306a36Sopenharmony_ci	asd_write_reg_byte(asd_ha, reg + 0x555, 0x55);
133162306a36Sopenharmony_ci	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90);
133262306a36Sopenharmony_ci	manuf_id = asd_read_reg_byte(asd_ha, reg);
133362306a36Sopenharmony_ci	dev_id = asd_read_reg_byte(asd_ha, reg + inc);
133462306a36Sopenharmony_ci	sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
133562306a36Sopenharmony_ci	/* Get out of autoselect mode. */
133662306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
133762306a36Sopenharmony_ci	if (err) {
133862306a36Sopenharmony_ci		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
133962306a36Sopenharmony_ci		return err;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci	ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) "
134262306a36Sopenharmony_ci		"sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot);
134362306a36Sopenharmony_ci	err = asd_reset_flash(asd_ha);
134462306a36Sopenharmony_ci	if (err != 0)
134562306a36Sopenharmony_ci		return err;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	switch (manuf_id) {
134862306a36Sopenharmony_ci	case FLASH_MANUF_ID_AMD:
134962306a36Sopenharmony_ci		switch (sec_prot) {
135062306a36Sopenharmony_ci		case FLASH_DEV_ID_AM29LV800DT:
135162306a36Sopenharmony_ci		case FLASH_DEV_ID_AM29LV640MT:
135262306a36Sopenharmony_ci		case FLASH_DEV_ID_AM29F800B:
135362306a36Sopenharmony_ci			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
135462306a36Sopenharmony_ci			break;
135562306a36Sopenharmony_ci		default:
135662306a36Sopenharmony_ci			break;
135762306a36Sopenharmony_ci		}
135862306a36Sopenharmony_ci		break;
135962306a36Sopenharmony_ci	case FLASH_MANUF_ID_ST:
136062306a36Sopenharmony_ci		switch (sec_prot) {
136162306a36Sopenharmony_ci		case FLASH_DEV_ID_STM29W800DT:
136262306a36Sopenharmony_ci		case FLASH_DEV_ID_STM29LV640:
136362306a36Sopenharmony_ci			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
136462306a36Sopenharmony_ci			break;
136562306a36Sopenharmony_ci		default:
136662306a36Sopenharmony_ci			break;
136762306a36Sopenharmony_ci		}
136862306a36Sopenharmony_ci		break;
136962306a36Sopenharmony_ci	case FLASH_MANUF_ID_FUJITSU:
137062306a36Sopenharmony_ci		switch (sec_prot) {
137162306a36Sopenharmony_ci		case FLASH_DEV_ID_MBM29LV800TE:
137262306a36Sopenharmony_ci		case FLASH_DEV_ID_MBM29DL800TA:
137362306a36Sopenharmony_ci			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
137462306a36Sopenharmony_ci			break;
137562306a36Sopenharmony_ci		}
137662306a36Sopenharmony_ci		break;
137762306a36Sopenharmony_ci	case FLASH_MANUF_ID_MACRONIX:
137862306a36Sopenharmony_ci		switch (sec_prot) {
137962306a36Sopenharmony_ci		case FLASH_DEV_ID_MX29LV800BT:
138062306a36Sopenharmony_ci			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
138162306a36Sopenharmony_ci			break;
138262306a36Sopenharmony_ci		}
138362306a36Sopenharmony_ci		break;
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) {
138762306a36Sopenharmony_ci		err = asd_reset_flash(asd_ha);
138862306a36Sopenharmony_ci		if (err) {
138962306a36Sopenharmony_ci			ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
139062306a36Sopenharmony_ci			return err;
139162306a36Sopenharmony_ci		}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		/* Issue Unlock sequence for AM29LV008BT */
139462306a36Sopenharmony_ci		asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
139562306a36Sopenharmony_ci		asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
139662306a36Sopenharmony_ci		asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90);
139762306a36Sopenharmony_ci		manuf_id = asd_read_reg_byte(asd_ha, reg);
139862306a36Sopenharmony_ci		dev_id = asd_read_reg_byte(asd_ha, reg + inc);
139962306a36Sopenharmony_ci		sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci		ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot"
140262306a36Sopenharmony_ci			"(0x%x)\n", manuf_id, dev_id, sec_prot);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		err = asd_reset_flash(asd_ha);
140562306a36Sopenharmony_ci		if (err != 0) {
140662306a36Sopenharmony_ci			ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
140762306a36Sopenharmony_ci			return err;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		switch (manuf_id) {
141162306a36Sopenharmony_ci		case FLASH_MANUF_ID_AMD:
141262306a36Sopenharmony_ci			switch (dev_id) {
141362306a36Sopenharmony_ci			case FLASH_DEV_ID_AM29LV008BT:
141462306a36Sopenharmony_ci				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
141562306a36Sopenharmony_ci				break;
141662306a36Sopenharmony_ci			default:
141762306a36Sopenharmony_ci				break;
141862306a36Sopenharmony_ci			}
141962306a36Sopenharmony_ci			break;
142062306a36Sopenharmony_ci		case FLASH_MANUF_ID_ST:
142162306a36Sopenharmony_ci			switch (dev_id) {
142262306a36Sopenharmony_ci			case FLASH_DEV_ID_STM29008:
142362306a36Sopenharmony_ci				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
142462306a36Sopenharmony_ci				break;
142562306a36Sopenharmony_ci			default:
142662306a36Sopenharmony_ci				break;
142762306a36Sopenharmony_ci			}
142862306a36Sopenharmony_ci			break;
142962306a36Sopenharmony_ci		case FLASH_MANUF_ID_FUJITSU:
143062306a36Sopenharmony_ci			switch (dev_id) {
143162306a36Sopenharmony_ci			case FLASH_DEV_ID_MBM29LV008TA:
143262306a36Sopenharmony_ci				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
143362306a36Sopenharmony_ci				break;
143462306a36Sopenharmony_ci			}
143562306a36Sopenharmony_ci			break;
143662306a36Sopenharmony_ci		case FLASH_MANUF_ID_INTEL:
143762306a36Sopenharmony_ci			switch (dev_id) {
143862306a36Sopenharmony_ci			case FLASH_DEV_ID_I28LV00TAT:
143962306a36Sopenharmony_ci				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
144062306a36Sopenharmony_ci				break;
144162306a36Sopenharmony_ci			}
144262306a36Sopenharmony_ci			break;
144362306a36Sopenharmony_ci		case FLASH_MANUF_ID_MACRONIX:
144462306a36Sopenharmony_ci			switch (dev_id) {
144562306a36Sopenharmony_ci			case FLASH_DEV_ID_I28LV00TAT:
144662306a36Sopenharmony_ci				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
144762306a36Sopenharmony_ci				break;
144862306a36Sopenharmony_ci			}
144962306a36Sopenharmony_ci			break;
145062306a36Sopenharmony_ci		default:
145162306a36Sopenharmony_ci			return FAIL_FIND_FLASH_ID;
145262306a36Sopenharmony_ci		}
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN)
145662306a36Sopenharmony_ci	      return FAIL_FIND_FLASH_ID;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	asd_ha->hw_prof.flash.manuf = manuf_id;
145962306a36Sopenharmony_ci	asd_ha->hw_prof.flash.dev_id = dev_id;
146062306a36Sopenharmony_ci	asd_ha->hw_prof.flash.sec_prot = sec_prot;
146162306a36Sopenharmony_ci	return 0;
146262306a36Sopenharmony_ci}
1463