165c85c83SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22908d778SJames Bottomley /*
32908d778SJames Bottomley  * Aic94xx SAS/SATA driver access to shared data structures and memory
42908d778SJames Bottomley  * maps.
52908d778SJames Bottomley  *
62908d778SJames Bottomley  * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
72908d778SJames Bottomley  * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
82908d778SJames Bottomley  */
92908d778SJames Bottomley 
102908d778SJames Bottomley #include <linux/pci.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
122908d778SJames Bottomley #include <linux/delay.h>
132908d778SJames Bottomley 
142908d778SJames Bottomley #include "aic94xx.h"
152908d778SJames Bottomley #include "aic94xx_reg.h"
161237c98dSGilbert Wu #include "aic94xx_sds.h"
172908d778SJames Bottomley 
182908d778SJames Bottomley /* ---------- OCM stuff ---------- */
192908d778SJames Bottomley 
202908d778SJames Bottomley struct asd_ocm_dir_ent {
212908d778SJames Bottomley 	u8 type;
222908d778SJames Bottomley 	u8 offs[3];
232908d778SJames Bottomley 	u8 _r1;
242908d778SJames Bottomley 	u8 size[3];
252908d778SJames Bottomley } __attribute__ ((packed));
262908d778SJames Bottomley 
272908d778SJames Bottomley struct asd_ocm_dir {
282908d778SJames Bottomley 	char sig[2];
292908d778SJames Bottomley 	u8   _r1[2];
302908d778SJames Bottomley 	u8   major;          /* 0 */
312908d778SJames Bottomley 	u8   minor;          /* 0 */
322908d778SJames Bottomley 	u8   _r2;
332908d778SJames Bottomley 	u8   num_de;
342908d778SJames Bottomley 	struct asd_ocm_dir_ent entry[15];
352908d778SJames Bottomley } __attribute__ ((packed));
362908d778SJames Bottomley 
372908d778SJames Bottomley #define	OCM_DE_OCM_DIR			0x00
382908d778SJames Bottomley #define	OCM_DE_WIN_DRVR			0x01
392908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM		0x02
402908d778SJames Bottomley #define	OCM_DE_RAID_ENGN		0x03
412908d778SJames Bottomley #define	OCM_DE_BIOS_INTL		0x04
422908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM_OSM		0x05
432908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM_DYNAMIC	0x06
442908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES0		0x07
452908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES1		0x08
462908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES2		0x09
472908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES3		0x0A
482908d778SJames Bottomley 
492908d778SJames Bottomley #define OCM_INIT_DIR_ENTRIES	5
502908d778SJames Bottomley /***************************************************************************
510779bf2dSMatt LaPlante *  OCM directory default
522908d778SJames Bottomley ***************************************************************************/
532908d778SJames Bottomley static struct asd_ocm_dir OCMDirInit =
542908d778SJames Bottomley {
552908d778SJames Bottomley 	.sig = {0x4D, 0x4F},	/* signature */
562908d778SJames Bottomley 	.num_de = OCM_INIT_DIR_ENTRIES,	/* no. of directory entries */
572908d778SJames Bottomley };
582908d778SJames Bottomley 
592908d778SJames Bottomley /***************************************************************************
600779bf2dSMatt LaPlante *  OCM directory Entries default
612908d778SJames Bottomley ***************************************************************************/
622908d778SJames Bottomley static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] =
632908d778SJames Bottomley {
642908d778SJames Bottomley 	{
652908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES0),	/* Entry type  */
662908d778SJames Bottomley 		.offs = {128},			/* Offset */
672908d778SJames Bottomley 		.size = {0, 4},			/* size */
682908d778SJames Bottomley 	},
692908d778SJames Bottomley 	{
702908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES1),	/* Entry type  */
712908d778SJames Bottomley 		.offs = {128, 4},		/* Offset */
722908d778SJames Bottomley 		.size = {0, 4},			/* size */
732908d778SJames Bottomley 	},
742908d778SJames Bottomley 	{
752908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES2),	/* Entry type  */
762908d778SJames Bottomley 		.offs = {128, 8},		/* Offset */
772908d778SJames Bottomley 		.size = {0, 4},			/* size */
782908d778SJames Bottomley 	},
792908d778SJames Bottomley 	{
802908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES3),	/* Entry type  */
812908d778SJames Bottomley 		.offs = {128, 12},		/* Offset */
822908d778SJames Bottomley 		.size = {0, 4},			/* size */
832908d778SJames Bottomley 	},
842908d778SJames Bottomley 	{
852908d778SJames Bottomley 		.type = (OCM_DE_WIN_DRVR),	/* Entry type  */
862908d778SJames Bottomley 		.offs = {128, 16},		/* Offset */
872908d778SJames Bottomley 		.size = {128, 235, 1},		/* size */
882908d778SJames Bottomley 	},
892908d778SJames Bottomley };
902908d778SJames Bottomley 
912908d778SJames Bottomley struct asd_bios_chim_struct {
922908d778SJames Bottomley 	char sig[4];
932908d778SJames Bottomley 	u8   major;          /* 1 */
942908d778SJames Bottomley 	u8   minor;          /* 0 */
952908d778SJames Bottomley 	u8   bios_major;
962908d778SJames Bottomley 	u8   bios_minor;
972908d778SJames Bottomley 	__le32  bios_build;
982908d778SJames Bottomley 	u8   flags;
992908d778SJames Bottomley 	u8   pci_slot;
1002908d778SJames Bottomley 	__le16  ue_num;
1012908d778SJames Bottomley 	__le16  ue_size;
1022908d778SJames Bottomley 	u8  _r[14];
1032908d778SJames Bottomley 	/* The unit element array is right here.
1042908d778SJames Bottomley 	 */
1052908d778SJames Bottomley } __attribute__ ((packed));
1062908d778SJames Bottomley 
1072908d778SJames Bottomley /**
1082908d778SJames Bottomley  * asd_read_ocm_seg - read an on chip memory (OCM) segment
1092908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
1102908d778SJames Bottomley  * @buffer: where to write the read data
1112908d778SJames Bottomley  * @offs: offset into OCM where to read from
1122908d778SJames Bottomley  * @size: how many bytes to read
1132908d778SJames Bottomley  *
1142908d778SJames Bottomley  * Return the number of bytes not read. Return 0 on success.
1152908d778SJames Bottomley  */
asd_read_ocm_seg(struct asd_ha_struct * asd_ha,void * buffer,u32 offs,int size)1162908d778SJames Bottomley static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
1172908d778SJames Bottomley 			    u32 offs, int size)
1182908d778SJames Bottomley {
1192908d778SJames Bottomley 	u8 *p = buffer;
1202908d778SJames Bottomley 	if (unlikely(asd_ha->iospace))
1212908d778SJames Bottomley 		asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
1222908d778SJames Bottomley 	else {
1232908d778SJames Bottomley 		for ( ; size > 0; size--, offs++, p++)
1242908d778SJames Bottomley 			*p = asd_read_ocm_byte(asd_ha, offs);
1252908d778SJames Bottomley 	}
1262908d778SJames Bottomley 	return size;
1272908d778SJames Bottomley }
1282908d778SJames Bottomley 
asd_read_ocm_dir(struct asd_ha_struct * asd_ha,struct asd_ocm_dir * dir,u32 offs)1292908d778SJames Bottomley static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha,
1302908d778SJames Bottomley 			    struct asd_ocm_dir *dir, u32 offs)
1312908d778SJames Bottomley {
1322908d778SJames Bottomley 	int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir));
1332908d778SJames Bottomley 	if (err) {
1342908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ocm segment\n");
1352908d778SJames Bottomley 		return err;
1362908d778SJames Bottomley 	}
1372908d778SJames Bottomley 
1382908d778SJames Bottomley 	if (dir->sig[0] != 'M' || dir->sig[1] != 'O') {
1392908d778SJames Bottomley 		ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n",
1402908d778SJames Bottomley 			    dir->sig[0], dir->sig[1]);
1412908d778SJames Bottomley 		return -ENOENT;
1422908d778SJames Bottomley 	}
1432908d778SJames Bottomley 	if (dir->major != 0) {
1442908d778SJames Bottomley 		asd_printk("unsupported major version of ocm dir:0x%x\n",
1452908d778SJames Bottomley 			   dir->major);
1462908d778SJames Bottomley 		return -ENOENT;
1472908d778SJames Bottomley 	}
1482908d778SJames Bottomley 	dir->num_de &= 0xf;
1492908d778SJames Bottomley 	return 0;
1502908d778SJames Bottomley }
1512908d778SJames Bottomley 
1522908d778SJames Bottomley /**
1532908d778SJames Bottomley  * asd_write_ocm_seg - write an on chip memory (OCM) segment
1542908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
1552908d778SJames Bottomley  * @buffer: where to read the write data
1562908d778SJames Bottomley  * @offs: offset into OCM to write to
1572908d778SJames Bottomley  * @size: how many bytes to write
1582908d778SJames Bottomley  *
1592908d778SJames Bottomley  * Return the number of bytes not written. Return 0 on success.
1602908d778SJames Bottomley  */
asd_write_ocm_seg(struct asd_ha_struct * asd_ha,void * buffer,u32 offs,int size)1612908d778SJames Bottomley static void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
1622908d778SJames Bottomley 			    u32 offs, int size)
1632908d778SJames Bottomley {
1642908d778SJames Bottomley 	u8 *p = buffer;
1652908d778SJames Bottomley 	if (unlikely(asd_ha->iospace))
1662908d778SJames Bottomley 		asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
1672908d778SJames Bottomley 	else {
1682908d778SJames Bottomley 		for ( ; size > 0; size--, offs++, p++)
1692908d778SJames Bottomley 			asd_write_ocm_byte(asd_ha, offs, *p);
1702908d778SJames Bottomley 	}
1712908d778SJames Bottomley 	return;
1722908d778SJames Bottomley }
1732908d778SJames Bottomley 
1742908d778SJames Bottomley #define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16))
1752908d778SJames Bottomley 
asd_find_dir_entry(struct asd_ocm_dir * dir,u8 type,u32 * offs,u32 * size)1762908d778SJames Bottomley static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type,
1772908d778SJames Bottomley 			      u32 *offs, u32 *size)
1782908d778SJames Bottomley {
1792908d778SJames Bottomley 	int i;
1802908d778SJames Bottomley 	struct asd_ocm_dir_ent *ent;
1812908d778SJames Bottomley 
1822908d778SJames Bottomley 	for (i = 0; i < dir->num_de; i++) {
1832908d778SJames Bottomley 		if (dir->entry[i].type == type)
1842908d778SJames Bottomley 			break;
1852908d778SJames Bottomley 	}
1862908d778SJames Bottomley 	if (i >= dir->num_de)
1872908d778SJames Bottomley 		return -ENOENT;
1882908d778SJames Bottomley 	ent = &dir->entry[i];
1892908d778SJames Bottomley 	*offs = (u32) THREE_TO_NUM(ent->offs);
1902908d778SJames Bottomley 	*size = (u32) THREE_TO_NUM(ent->size);
1912908d778SJames Bottomley 	return 0;
1922908d778SJames Bottomley }
1932908d778SJames Bottomley 
1942908d778SJames Bottomley #define OCM_BIOS_CHIM_DE  2
1952908d778SJames Bottomley #define BC_BIOS_PRESENT   1
1962908d778SJames Bottomley 
asd_get_bios_chim(struct asd_ha_struct * asd_ha,struct asd_ocm_dir * dir)1972908d778SJames Bottomley static int asd_get_bios_chim(struct asd_ha_struct *asd_ha,
1982908d778SJames Bottomley 			     struct asd_ocm_dir *dir)
1992908d778SJames Bottomley {
2002908d778SJames Bottomley 	int err;
2012908d778SJames Bottomley 	struct asd_bios_chim_struct *bc_struct;
2022908d778SJames Bottomley 	u32 offs, size;
2032908d778SJames Bottomley 
2042908d778SJames Bottomley 	err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size);
2052908d778SJames Bottomley 	if (err) {
2062908d778SJames Bottomley 		ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n");
2072908d778SJames Bottomley 		goto out;
2082908d778SJames Bottomley 	}
2092908d778SJames Bottomley 	err = -ENOMEM;
2102908d778SJames Bottomley 	bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL);
2112908d778SJames Bottomley 	if (!bc_struct) {
2122908d778SJames Bottomley 		asd_printk("no memory for bios_chim struct\n");
2132908d778SJames Bottomley 		goto out;
2142908d778SJames Bottomley 	}
2152908d778SJames Bottomley 	err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs,
2162908d778SJames Bottomley 			       sizeof(*bc_struct));
2172908d778SJames Bottomley 	if (err) {
2182908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ocm segment\n");
2192908d778SJames Bottomley 		goto out2;
2202908d778SJames Bottomley 	}
2212908d778SJames Bottomley 	if (strncmp(bc_struct->sig, "SOIB", 4)
2222908d778SJames Bottomley 	    && strncmp(bc_struct->sig, "IPSA", 4)) {
2232908d778SJames Bottomley 		ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n",
2242908d778SJames Bottomley 			    bc_struct->sig[0], bc_struct->sig[1],
2252908d778SJames Bottomley 			    bc_struct->sig[2], bc_struct->sig[3]);
2262908d778SJames Bottomley 		err = -ENOENT;
2272908d778SJames Bottomley 		goto out2;
2282908d778SJames Bottomley 	}
2292908d778SJames Bottomley 	if (bc_struct->major != 1) {
2302908d778SJames Bottomley 		asd_printk("BIOS_CHIM unsupported major version:0x%x\n",
2312908d778SJames Bottomley 			   bc_struct->major);
2322908d778SJames Bottomley 		err = -ENOENT;
2332908d778SJames Bottomley 		goto out2;
2342908d778SJames Bottomley 	}
2352908d778SJames Bottomley 	if (bc_struct->flags & BC_BIOS_PRESENT) {
2362908d778SJames Bottomley 		asd_ha->hw_prof.bios.present = 1;
2372908d778SJames Bottomley 		asd_ha->hw_prof.bios.maj = bc_struct->bios_major;
2382908d778SJames Bottomley 		asd_ha->hw_prof.bios.min = bc_struct->bios_minor;
2392908d778SJames Bottomley 		asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build);
2402908d778SJames Bottomley 		ASD_DPRINTK("BIOS present (%d,%d), %d\n",
2412908d778SJames Bottomley 			    asd_ha->hw_prof.bios.maj,
2422908d778SJames Bottomley 			    asd_ha->hw_prof.bios.min,
2432908d778SJames Bottomley 			    asd_ha->hw_prof.bios.bld);
2442908d778SJames Bottomley 	}
2452908d778SJames Bottomley 	asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num);
2462908d778SJames Bottomley 	asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size);
2472908d778SJames Bottomley 	ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num,
2482908d778SJames Bottomley 		    asd_ha->hw_prof.ue.size);
2492908d778SJames Bottomley 	size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size;
2502908d778SJames Bottomley 	if (size > 0) {
2512908d778SJames Bottomley 		err = -ENOMEM;
2522908d778SJames Bottomley 		asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL);
2532908d778SJames Bottomley 		if (!asd_ha->hw_prof.ue.area)
2542908d778SJames Bottomley 			goto out2;
2552908d778SJames Bottomley 		err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area,
2562908d778SJames Bottomley 				       offs + sizeof(*bc_struct), size);
2572908d778SJames Bottomley 		if (err) {
2582908d778SJames Bottomley 			kfree(asd_ha->hw_prof.ue.area);
2592908d778SJames Bottomley 			asd_ha->hw_prof.ue.area = NULL;
2602908d778SJames Bottomley 			asd_ha->hw_prof.ue.num  = 0;
2612908d778SJames Bottomley 			asd_ha->hw_prof.ue.size = 0;
2622908d778SJames Bottomley 			ASD_DPRINTK("couldn't read ue entries(%d)\n", err);
2632908d778SJames Bottomley 		}
2642908d778SJames Bottomley 	}
2652908d778SJames Bottomley out2:
2662908d778SJames Bottomley 	kfree(bc_struct);
2672908d778SJames Bottomley out:
2682908d778SJames Bottomley 	return err;
2692908d778SJames Bottomley }
2702908d778SJames Bottomley 
2712908d778SJames Bottomley static void
asd_hwi_initialize_ocm_dir(struct asd_ha_struct * asd_ha)2722908d778SJames Bottomley asd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha)
2732908d778SJames Bottomley {
2742908d778SJames Bottomley 	int i;
2752908d778SJames Bottomley 
2762908d778SJames Bottomley 	/* Zero OCM */
2772908d778SJames Bottomley 	for (i = 0; i < OCM_MAX_SIZE; i += 4)
2782908d778SJames Bottomley 		asd_write_ocm_dword(asd_ha, i, 0);
2792908d778SJames Bottomley 
2802908d778SJames Bottomley 	/* Write Dir */
2812908d778SJames Bottomley 	asd_write_ocm_seg(asd_ha, &OCMDirInit, 0,
2822908d778SJames Bottomley 			  sizeof(struct asd_ocm_dir));
2832908d778SJames Bottomley 
2842908d778SJames Bottomley 	/* Write Dir Entries */
2852908d778SJames Bottomley 	for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++)
2862908d778SJames Bottomley 		asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i],
2872908d778SJames Bottomley 				  sizeof(struct asd_ocm_dir) +
2882908d778SJames Bottomley 				  (i * sizeof(struct asd_ocm_dir_ent))
2892908d778SJames Bottomley 				  , sizeof(struct asd_ocm_dir_ent));
2902908d778SJames Bottomley 
2912908d778SJames Bottomley }
2922908d778SJames Bottomley 
2932908d778SJames Bottomley static int
asd_hwi_check_ocm_access(struct asd_ha_struct * asd_ha)2942908d778SJames Bottomley asd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha)
2952908d778SJames Bottomley {
2962908d778SJames Bottomley 	struct pci_dev *pcidev = asd_ha->pcidev;
2972908d778SJames Bottomley 	u32 reg;
2982908d778SJames Bottomley 	int err = 0;
2992908d778SJames Bottomley 	u32 v;
3002908d778SJames Bottomley 
3012908d778SJames Bottomley 	/* check if OCM has been initialized by BIOS */
3022908d778SJames Bottomley 	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
3032908d778SJames Bottomley 
3042908d778SJames Bottomley 	if (!(reg & OCMINITIALIZED)) {
3052908d778SJames Bottomley 		err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v);
3062908d778SJames Bottomley 		if (err) {
3072908d778SJames Bottomley 			asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n",
3082908d778SJames Bottomley 					pci_name(pcidev));
3092908d778SJames Bottomley 			goto out;
3102908d778SJames Bottomley 		}
3112908d778SJames Bottomley 
3122908d778SJames Bottomley 		printk(KERN_INFO "OCM is not initialized by BIOS,"
3132908d778SJames Bottomley 		       "reinitialize it and ignore it, current IntrptStatus"
3142908d778SJames Bottomley 		       "is 0x%x\n", v);
3152908d778SJames Bottomley 
3162908d778SJames Bottomley 		if (v)
3172908d778SJames Bottomley 			err = pci_write_config_dword(pcidev,
3182908d778SJames Bottomley 						     PCIC_INTRPT_STAT, v);
3192908d778SJames Bottomley 		if (err) {
3202908d778SJames Bottomley 			asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n",
3212908d778SJames Bottomley 					pci_name(pcidev));
3222908d778SJames Bottomley 			goto out;
3232908d778SJames Bottomley 		}
3242908d778SJames Bottomley 
3252908d778SJames Bottomley 		asd_hwi_initialize_ocm_dir(asd_ha);
3262908d778SJames Bottomley 
3272908d778SJames Bottomley 	}
3282908d778SJames Bottomley out:
3292908d778SJames Bottomley 	return err;
3302908d778SJames Bottomley }
3312908d778SJames Bottomley 
3322908d778SJames Bottomley /**
3332908d778SJames Bottomley  * asd_read_ocm - read on chip memory (OCM)
3342908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
3352908d778SJames Bottomley  */
asd_read_ocm(struct asd_ha_struct * asd_ha)3362908d778SJames Bottomley int asd_read_ocm(struct asd_ha_struct *asd_ha)
3372908d778SJames Bottomley {
3382908d778SJames Bottomley 	int err;
3392908d778SJames Bottomley 	struct asd_ocm_dir *dir;
3402908d778SJames Bottomley 
3412908d778SJames Bottomley 	if (asd_hwi_check_ocm_access(asd_ha))
3422908d778SJames Bottomley 		return -1;
3432908d778SJames Bottomley 
3442908d778SJames Bottomley 	dir = kmalloc(sizeof(*dir), GFP_KERNEL);
3452908d778SJames Bottomley 	if (!dir) {
3462908d778SJames Bottomley 		asd_printk("no memory for ocm dir\n");
3472908d778SJames Bottomley 		return -ENOMEM;
3482908d778SJames Bottomley 	}
3492908d778SJames Bottomley 
3502908d778SJames Bottomley 	err = asd_read_ocm_dir(asd_ha, dir, 0);
3512908d778SJames Bottomley 	if (err)
3522908d778SJames Bottomley 		goto out;
3532908d778SJames Bottomley 
3542908d778SJames Bottomley 	err = asd_get_bios_chim(asd_ha, dir);
3552908d778SJames Bottomley out:
3562908d778SJames Bottomley 	kfree(dir);
3572908d778SJames Bottomley 	return err;
3582908d778SJames Bottomley }
3592908d778SJames Bottomley 
3602908d778SJames Bottomley /* ---------- FLASH stuff ---------- */
3612908d778SJames Bottomley 
3622908d778SJames Bottomley #define FLASH_RESET			0xF0
3632908d778SJames Bottomley 
364d297a5d5SAndrew Morton #define ASD_FLASH_SIZE                  0x200000
3652908d778SJames Bottomley #define FLASH_DIR_COOKIE                "*** ADAPTEC FLASH DIRECTORY *** "
3662908d778SJames Bottomley #define FLASH_NEXT_ENTRY_OFFS		0x2000
3672908d778SJames Bottomley #define FLASH_MAX_DIR_ENTRIES		32
3682908d778SJames Bottomley 
3692908d778SJames Bottomley #define FLASH_DE_TYPE_MASK              0x3FFFFFFF
3702908d778SJames Bottomley #define FLASH_DE_MS                     0x120
3712908d778SJames Bottomley #define FLASH_DE_CTRL_A_USER            0xE0
3722908d778SJames Bottomley 
3732908d778SJames Bottomley struct asd_flash_de {
3742908d778SJames Bottomley 	__le32   type;
3752908d778SJames Bottomley 	__le32   offs;
3762908d778SJames Bottomley 	__le32   pad_size;
3772908d778SJames Bottomley 	__le32   image_size;
3782908d778SJames Bottomley 	__le32   chksum;
3792908d778SJames Bottomley 	u8       _r[12];
3802908d778SJames Bottomley 	u8       version[32];
3812908d778SJames Bottomley } __attribute__ ((packed));
3822908d778SJames Bottomley 
3832908d778SJames Bottomley struct asd_flash_dir {
3842908d778SJames Bottomley 	u8    cookie[32];
3852908d778SJames Bottomley 	__le32   rev;		  /* 2 */
3862908d778SJames Bottomley 	__le32   chksum;
3872908d778SJames Bottomley 	__le32   chksum_antidote;
3882908d778SJames Bottomley 	__le32   bld;
3892908d778SJames Bottomley 	u8    bld_id[32];	  /* build id data */
3902908d778SJames Bottomley 	u8    ver_data[32];	  /* date and time of build */
3912908d778SJames Bottomley 	__le32   ae_mask;
3922908d778SJames Bottomley 	__le32   v_mask;
3932908d778SJames Bottomley 	__le32   oc_mask;
3942908d778SJames Bottomley 	u8    _r[20];
3952908d778SJames Bottomley 	struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES];
3962908d778SJames Bottomley } __attribute__ ((packed));
3972908d778SJames Bottomley 
3982908d778SJames Bottomley struct asd_manuf_sec {
3992908d778SJames Bottomley 	char  sig[2];		  /* 'S', 'M' */
4002908d778SJames Bottomley 	u16   offs_next;
4012908d778SJames Bottomley 	u8    maj;           /* 0 */
4022908d778SJames Bottomley 	u8    min;           /* 0 */
4032908d778SJames Bottomley 	u16   chksum;
4042908d778SJames Bottomley 	u16   size;
4052908d778SJames Bottomley 	u8    _r[6];
4062908d778SJames Bottomley 	u8    sas_addr[SAS_ADDR_SIZE];
4072908d778SJames Bottomley 	u8    pcba_sn[ASD_PCBA_SN_SIZE];
4082908d778SJames Bottomley 	/* Here start the other segments */
40900b42b70SGustavo A. R. Silva 	u8    linked_list[];
4102908d778SJames Bottomley } __attribute__ ((packed));
4112908d778SJames Bottomley 
4122908d778SJames Bottomley struct asd_manuf_phy_desc {
4132908d778SJames Bottomley 	u8    state;         /* low 4 bits */
41486b9c4c1SAlexis Bruemmer #define MS_PHY_STATE_ENABLED    0
4152908d778SJames Bottomley #define MS_PHY_STATE_REPORTED   1
4162908d778SJames Bottomley #define MS_PHY_STATE_HIDDEN     2
4172908d778SJames Bottomley 	u8    phy_id;
4182908d778SJames Bottomley 	u16   _r;
4192908d778SJames Bottomley 	u8    phy_control_0; /* mode 5 reg 0x160 */
4202908d778SJames Bottomley 	u8    phy_control_1; /* mode 5 reg 0x161 */
4212908d778SJames Bottomley 	u8    phy_control_2; /* mode 5 reg 0x162 */
4222908d778SJames Bottomley 	u8    phy_control_3; /* mode 5 reg 0x163 */
4232908d778SJames Bottomley } __attribute__ ((packed));
4242908d778SJames Bottomley 
4252908d778SJames Bottomley struct asd_manuf_phy_param {
4262908d778SJames Bottomley 	char  sig[2];		  /* 'P', 'M' */
4272908d778SJames Bottomley 	u16   next;
4282908d778SJames Bottomley 	u8    maj;           /* 0 */
4292908d778SJames Bottomley 	u8    min;           /* 2 */
4302908d778SJames Bottomley 	u8    num_phy_desc;  /* 8 */
4312908d778SJames Bottomley 	u8    phy_desc_size; /* 8 */
4322908d778SJames Bottomley 	u8    _r[3];
4332908d778SJames Bottomley 	u8    usage_model_id;
4342908d778SJames Bottomley 	u32   _r2;
4352908d778SJames Bottomley 	struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS];
4362908d778SJames Bottomley } __attribute__ ((packed));
4372908d778SJames Bottomley 
4382908d778SJames Bottomley #if 0
4392908d778SJames Bottomley static const char *asd_sb_type[] = {
4402908d778SJames Bottomley 	"unknown",
4412908d778SJames Bottomley 	"SGPIO",
4422908d778SJames Bottomley 	[2 ... 0x7F] = "unknown",
4432908d778SJames Bottomley 	[0x80] = "ADPT_I2C",
4442908d778SJames Bottomley 	[0x81 ... 0xFF] = "VENDOR_UNIQUExx"
4452908d778SJames Bottomley };
4462908d778SJames Bottomley #endif
4472908d778SJames Bottomley 
4482908d778SJames Bottomley struct asd_ms_sb_desc {
4492908d778SJames Bottomley 	u8    type;
4502908d778SJames Bottomley 	u8    node_desc_index;
4512908d778SJames Bottomley 	u8    conn_desc_index;
45200b42b70SGustavo A. R. Silva 	u8    _recvd[];
4532908d778SJames Bottomley } __attribute__ ((packed));
4542908d778SJames Bottomley 
4552908d778SJames Bottomley #if 0
4562908d778SJames Bottomley static const char *asd_conn_type[] = {
4572908d778SJames Bottomley 	[0 ... 7] = "unknown",
4582908d778SJames Bottomley 	"SFF8470",
4592908d778SJames Bottomley 	"SFF8482",
4602908d778SJames Bottomley 	"SFF8484",
4612908d778SJames Bottomley 	[0x80] = "PCIX_DAUGHTER0",
4622908d778SJames Bottomley 	[0x81] = "SAS_DAUGHTER0",
4632908d778SJames Bottomley 	[0x82 ... 0xFF] = "VENDOR_UNIQUExx"
4642908d778SJames Bottomley };
4652908d778SJames Bottomley 
4662908d778SJames Bottomley static const char *asd_conn_location[] = {
4672908d778SJames Bottomley 	"unknown",
4682908d778SJames Bottomley 	"internal",
4692908d778SJames Bottomley 	"external",
4702908d778SJames Bottomley 	"board_to_board",
4712908d778SJames Bottomley };
4722908d778SJames Bottomley #endif
4732908d778SJames Bottomley 
4742908d778SJames Bottomley struct asd_ms_conn_desc {
4752908d778SJames Bottomley 	u8    type;
4762908d778SJames Bottomley 	u8    location;
4772908d778SJames Bottomley 	u8    num_sideband_desc;
4782908d778SJames Bottomley 	u8    size_sideband_desc;
4792908d778SJames Bottomley 	u32   _resvd;
4802908d778SJames Bottomley 	u8    name[16];
48100b42b70SGustavo A. R. Silva 	struct asd_ms_sb_desc sb_desc[];
4822908d778SJames Bottomley } __attribute__ ((packed));
4832908d778SJames Bottomley 
4842908d778SJames Bottomley struct asd_nd_phy_desc {
4852908d778SJames Bottomley 	u8    vp_attch_type;
48600b42b70SGustavo A. R. Silva 	u8    attch_specific[];
4872908d778SJames Bottomley } __attribute__ ((packed));
4882908d778SJames Bottomley 
4892908d778SJames Bottomley #if 0
4902908d778SJames Bottomley static const char *asd_node_type[] = {
4912908d778SJames Bottomley 	"IOP",
4922908d778SJames Bottomley 	"IO_CONTROLLER",
4932908d778SJames Bottomley 	"EXPANDER",
4942908d778SJames Bottomley 	"PORT_MULTIPLIER",
4952908d778SJames Bottomley 	"PORT_MULTIPLEXER",
4962908d778SJames Bottomley 	"MULTI_DROP_I2C_BUS",
4972908d778SJames Bottomley };
4982908d778SJames Bottomley #endif
4992908d778SJames Bottomley 
5002908d778SJames Bottomley struct asd_ms_node_desc {
5012908d778SJames Bottomley 	u8    type;
5022908d778SJames Bottomley 	u8    num_phy_desc;
5032908d778SJames Bottomley 	u8    size_phy_desc;
5042908d778SJames Bottomley 	u8    _resvd;
5052908d778SJames Bottomley 	u8    name[16];
50600b42b70SGustavo A. R. Silva 	struct asd_nd_phy_desc phy_desc[];
5072908d778SJames Bottomley } __attribute__ ((packed));
5082908d778SJames Bottomley 
5092908d778SJames Bottomley struct asd_ms_conn_map {
5102908d778SJames Bottomley 	char  sig[2];		  /* 'M', 'C' */
5112908d778SJames Bottomley 	__le16 next;
5122908d778SJames Bottomley 	u8    maj;		  /* 0 */
5132908d778SJames Bottomley 	u8    min;		  /* 0 */
5142908d778SJames Bottomley 	__le16 cm_size;		  /* size of this struct */
5152908d778SJames Bottomley 	u8    num_conn;
5162908d778SJames Bottomley 	u8    conn_size;
5172908d778SJames Bottomley 	u8    num_nodes;
5182908d778SJames Bottomley 	u8    usage_model_id;
5192908d778SJames Bottomley 	u32   _resvd;
520*fa7845cfSKees Cook 	union {
521*fa7845cfSKees Cook 		DECLARE_FLEX_ARRAY(struct asd_ms_conn_desc, conn_desc);
522*fa7845cfSKees Cook 		DECLARE_FLEX_ARRAY(struct asd_ms_node_desc, node_desc);
523*fa7845cfSKees Cook 	};
5242908d778SJames Bottomley } __attribute__ ((packed));
5252908d778SJames Bottomley 
5262908d778SJames Bottomley struct asd_ctrla_phy_entry {
5272908d778SJames Bottomley 	u8    sas_addr[SAS_ADDR_SIZE];
5282908d778SJames Bottomley 	u8    sas_link_rates;  /* max in hi bits, min in low bits */
5292908d778SJames Bottomley 	u8    flags;
5302908d778SJames Bottomley 	u8    sata_link_rates;
5312908d778SJames Bottomley 	u8    _r[5];
5322908d778SJames Bottomley } __attribute__ ((packed));
5332908d778SJames Bottomley 
5342908d778SJames Bottomley struct asd_ctrla_phy_settings {
5352908d778SJames Bottomley 	u8    id0;		  /* P'h'y */
5362908d778SJames Bottomley 	u8    _r;
5372908d778SJames Bottomley 	u16   next;
5382908d778SJames Bottomley 	u8    num_phys;	      /* number of PHYs in the PCI function */
5392908d778SJames Bottomley 	u8    _r2[3];
5402908d778SJames Bottomley 	struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS];
5412908d778SJames Bottomley } __attribute__ ((packed));
5422908d778SJames Bottomley 
5432908d778SJames Bottomley struct asd_ll_el {
5442908d778SJames Bottomley 	u8   id0;
5452908d778SJames Bottomley 	u8   id1;
5462908d778SJames Bottomley 	__le16  next;
54700b42b70SGustavo A. R. Silva 	u8   something_here[];
5482908d778SJames Bottomley } __attribute__ ((packed));
5492908d778SJames Bottomley 
asd_poll_flash(struct asd_ha_struct * asd_ha)5502908d778SJames Bottomley static int asd_poll_flash(struct asd_ha_struct *asd_ha)
5512908d778SJames Bottomley {
5522908d778SJames Bottomley 	int c;
5532908d778SJames Bottomley 	u8 d;
5542908d778SJames Bottomley 
5552908d778SJames Bottomley 	for (c = 5000; c > 0; c--) {
5562908d778SJames Bottomley 		d  = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
5572908d778SJames Bottomley 		d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
5582908d778SJames Bottomley 		if (!d)
5592908d778SJames Bottomley 			return 0;
5602908d778SJames Bottomley 		udelay(5);
5612908d778SJames Bottomley 	}
5622908d778SJames Bottomley 	return -ENOENT;
5632908d778SJames Bottomley }
5642908d778SJames Bottomley 
asd_reset_flash(struct asd_ha_struct * asd_ha)5652908d778SJames Bottomley static int asd_reset_flash(struct asd_ha_struct *asd_ha)
5662908d778SJames Bottomley {
5672908d778SJames Bottomley 	int err;
5682908d778SJames Bottomley 
5692908d778SJames Bottomley 	err = asd_poll_flash(asd_ha);
5702908d778SJames Bottomley 	if (err)
5712908d778SJames Bottomley 		return err;
5722908d778SJames Bottomley 	asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET);
5732908d778SJames Bottomley 	err = asd_poll_flash(asd_ha);
5742908d778SJames Bottomley 
5752908d778SJames Bottomley 	return err;
5762908d778SJames Bottomley }
5772908d778SJames Bottomley 
asd_read_flash_seg(struct asd_ha_struct * asd_ha,void * buffer,u32 offs,int size)57881e56dedSAdrian Bunk static int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
5792908d778SJames Bottomley 			      void *buffer, u32 offs, int size)
5802908d778SJames Bottomley {
5812908d778SJames Bottomley 	asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
5822908d778SJames Bottomley 			    size);
5832908d778SJames Bottomley 	return 0;
5842908d778SJames Bottomley }
5852908d778SJames Bottomley 
5862908d778SJames Bottomley /**
5872908d778SJames Bottomley  * asd_find_flash_dir - finds and reads the flash directory
5882908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
5892908d778SJames Bottomley  * @flash_dir: pointer to flash directory structure
5902908d778SJames Bottomley  *
5912908d778SJames Bottomley  * If found, the flash directory segment will be copied to
5922908d778SJames Bottomley  * @flash_dir.  Return 1 if found, 0 if not.
5932908d778SJames Bottomley  */
asd_find_flash_dir(struct asd_ha_struct * asd_ha,struct asd_flash_dir * flash_dir)5942908d778SJames Bottomley static int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
5952908d778SJames Bottomley 			      struct asd_flash_dir *flash_dir)
5962908d778SJames Bottomley {
5972908d778SJames Bottomley 	u32 v;
598d297a5d5SAndrew Morton 	for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
5992908d778SJames Bottomley 		asd_read_flash_seg(asd_ha, flash_dir, v,
6002908d778SJames Bottomley 				   sizeof(FLASH_DIR_COOKIE)-1);
6012908d778SJames Bottomley 		if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
6022908d778SJames Bottomley 			   sizeof(FLASH_DIR_COOKIE)-1) == 0) {
6032908d778SJames Bottomley 			asd_ha->hw_prof.flash.dir_offs = v;
6042908d778SJames Bottomley 			asd_read_flash_seg(asd_ha, flash_dir, v,
6052908d778SJames Bottomley 					   sizeof(*flash_dir));
6062908d778SJames Bottomley 			return 1;
6072908d778SJames Bottomley 		}
6082908d778SJames Bottomley 	}
6092908d778SJames Bottomley 	return 0;
6102908d778SJames Bottomley }
6112908d778SJames Bottomley 
asd_flash_getid(struct asd_ha_struct * asd_ha)6122908d778SJames Bottomley static int asd_flash_getid(struct asd_ha_struct *asd_ha)
6132908d778SJames Bottomley {
6142908d778SJames Bottomley 	int err = 0;
615f2d719c6SAlexis Bruemmer 	u32 reg;
6162908d778SJames Bottomley 
6172908d778SJames Bottomley 	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
6182908d778SJames Bottomley 
6192908d778SJames Bottomley 	if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
6202908d778SJames Bottomley 				  &asd_ha->hw_prof.flash.bar)) {
6212908d778SJames Bottomley 		asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
6222908d778SJames Bottomley 			   pci_name(asd_ha->pcidev));
6232908d778SJames Bottomley 		return -ENOENT;
6242908d778SJames Bottomley 	}
6252908d778SJames Bottomley 	asd_ha->hw_prof.flash.present = 1;
6262908d778SJames Bottomley 	asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0;
6272908d778SJames Bottomley 	err = asd_reset_flash(asd_ha);
6282908d778SJames Bottomley 	if (err) {
6292908d778SJames Bottomley 		ASD_DPRINTK("couldn't reset flash(%d)\n", err);
6302908d778SJames Bottomley 		return err;
6312908d778SJames Bottomley 	}
6322908d778SJames Bottomley 	return 0;
6332908d778SJames Bottomley }
6342908d778SJames Bottomley 
asd_calc_flash_chksum(u16 * p,int size)6352908d778SJames Bottomley static u16 asd_calc_flash_chksum(u16 *p, int size)
6362908d778SJames Bottomley {
6372908d778SJames Bottomley 	u16 chksum = 0;
6382908d778SJames Bottomley 
6392908d778SJames Bottomley 	while (size-- > 0)
6402908d778SJames Bottomley 		chksum += *p++;
6412908d778SJames Bottomley 
6422908d778SJames Bottomley 	return chksum;
6432908d778SJames Bottomley }
6442908d778SJames Bottomley 
6452908d778SJames Bottomley 
asd_find_flash_de(struct asd_flash_dir * flash_dir,u32 entry_type,u32 * offs,u32 * size)6462908d778SJames Bottomley static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type,
6472908d778SJames Bottomley 			     u32 *offs, u32 *size)
6482908d778SJames Bottomley {
6492908d778SJames Bottomley 	int i;
6502908d778SJames Bottomley 	struct asd_flash_de *de;
6512908d778SJames Bottomley 
6522908d778SJames Bottomley 	for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) {
6532908d778SJames Bottomley 		u32 type = le32_to_cpu(flash_dir->dir_entry[i].type);
6542908d778SJames Bottomley 
6552908d778SJames Bottomley 		type &= FLASH_DE_TYPE_MASK;
6562908d778SJames Bottomley 		if (type == entry_type)
6572908d778SJames Bottomley 			break;
6582908d778SJames Bottomley 	}
6592908d778SJames Bottomley 	if (i >= FLASH_MAX_DIR_ENTRIES)
6602908d778SJames Bottomley 		return -ENOENT;
6612908d778SJames Bottomley 	de = &flash_dir->dir_entry[i];
6622908d778SJames Bottomley 	*offs = le32_to_cpu(de->offs);
6632908d778SJames Bottomley 	*size = le32_to_cpu(de->pad_size);
6642908d778SJames Bottomley 	return 0;
6652908d778SJames Bottomley }
6662908d778SJames Bottomley 
asd_validate_ms(struct asd_manuf_sec * ms)6672908d778SJames Bottomley static int asd_validate_ms(struct asd_manuf_sec *ms)
6682908d778SJames Bottomley {
6692908d778SJames Bottomley 	if (ms->sig[0] != 'S' || ms->sig[1] != 'M') {
6702908d778SJames Bottomley 		ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n",
6712908d778SJames Bottomley 			    ms->sig[0], ms->sig[1]);
6722908d778SJames Bottomley 		return -ENOENT;
6732908d778SJames Bottomley 	}
6742908d778SJames Bottomley 	if (ms->maj != 0) {
6752908d778SJames Bottomley 		asd_printk("unsupported manuf. sector. major version:%x\n",
6762908d778SJames Bottomley 			   ms->maj);
6772908d778SJames Bottomley 		return -ENOENT;
6782908d778SJames Bottomley 	}
6792908d778SJames Bottomley 	ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next);
6802908d778SJames Bottomley 	ms->chksum = le16_to_cpu((__force __le16) ms->chksum);
6812908d778SJames Bottomley 	ms->size = le16_to_cpu((__force __le16) ms->size);
6822908d778SJames Bottomley 
6832908d778SJames Bottomley 	if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) {
6842908d778SJames Bottomley 		asd_printk("failed manuf sector checksum\n");
6852908d778SJames Bottomley 	}
6862908d778SJames Bottomley 
6872908d778SJames Bottomley 	return 0;
6882908d778SJames Bottomley }
6892908d778SJames Bottomley 
asd_ms_get_sas_addr(struct asd_ha_struct * asd_ha,struct asd_manuf_sec * ms)6902908d778SJames Bottomley static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha,
6912908d778SJames Bottomley 			       struct asd_manuf_sec *ms)
6922908d778SJames Bottomley {
6932908d778SJames Bottomley 	memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE);
6942908d778SJames Bottomley 	return 0;
6952908d778SJames Bottomley }
6962908d778SJames Bottomley 
asd_ms_get_pcba_sn(struct asd_ha_struct * asd_ha,struct asd_manuf_sec * ms)6972908d778SJames Bottomley static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha,
6982908d778SJames Bottomley 			      struct asd_manuf_sec *ms)
6992908d778SJames Bottomley {
7002908d778SJames Bottomley 	memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE);
7012908d778SJames Bottomley 	asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0';
7022908d778SJames Bottomley 	return 0;
7032908d778SJames Bottomley }
7042908d778SJames Bottomley 
7052908d778SJames Bottomley /**
7062908d778SJames Bottomley  * asd_find_ll_by_id - find a linked list entry by its id
7072908d778SJames Bottomley  * @start: void pointer to the first element in the linked list
7082908d778SJames Bottomley  * @id0: the first byte of the id  (offs 0)
7092908d778SJames Bottomley  * @id1: the second byte of the id (offs 1)
7102908d778SJames Bottomley  *
7112908d778SJames Bottomley  * @start has to be the _base_ element start, since the
7122908d778SJames Bottomley  * linked list entries's offset is from this pointer.
7132908d778SJames Bottomley  * Some linked list entries use only the first id, in which case
7142908d778SJames Bottomley  * you can pass 0xFF for the second.
7152908d778SJames Bottomley  */
asd_find_ll_by_id(void * const start,const u8 id0,const u8 id1)7162908d778SJames Bottomley static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
7172908d778SJames Bottomley {
7182908d778SJames Bottomley 	struct asd_ll_el *el = start;
7192908d778SJames Bottomley 
7202908d778SJames Bottomley 	do {
7212908d778SJames Bottomley 		switch (id1) {
7222908d778SJames Bottomley 		default:
7234e1c8c17SGustavo A. R. Silva 			if (el->id1 == id1) {
7244e1c8c17SGustavo A. R. Silva 			fallthrough;
7252908d778SJames Bottomley 		case 0xFF:
7262908d778SJames Bottomley 				if (el->id0 == id0)
7272908d778SJames Bottomley 					return el;
7282908d778SJames Bottomley 			}
7294e1c8c17SGustavo A. R. Silva 		}
7302908d778SJames Bottomley 		el = start + le16_to_cpu(el->next);
7312908d778SJames Bottomley 	} while (el != start);
7322908d778SJames Bottomley 
7332908d778SJames Bottomley 	return NULL;
7342908d778SJames Bottomley }
7352908d778SJames Bottomley 
7362908d778SJames Bottomley /**
7372908d778SJames Bottomley  * asd_ms_get_phy_params - get phy parameters from the manufacturing sector
7382908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
7392908d778SJames Bottomley  * @manuf_sec: pointer to the manufacturing sector
7402908d778SJames Bottomley  *
7412908d778SJames Bottomley  * The manufacturing sector contans also the linked list of sub-segments,
7422908d778SJames Bottomley  * since when it was read, its size was taken from the flash directory,
7432908d778SJames Bottomley  * not from the structure size.
7442908d778SJames Bottomley  *
7452908d778SJames Bottomley  * HIDDEN phys do not count in the total count.  REPORTED phys cannot
7462908d778SJames Bottomley  * be enabled but are reported and counted towards the total.
74786b9c4c1SAlexis Bruemmer  * ENABLED phys are enabled by default and count towards the total.
7482908d778SJames Bottomley  * The absolute total phy number is ASD_MAX_PHYS.  hw_prof->num_phys
7492908d778SJames Bottomley  * merely specifies the number of phys the host adapter decided to
7502908d778SJames Bottomley  * report.  E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
75186b9c4c1SAlexis Bruemmer  * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED.
7522908d778SJames Bottomley  * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
7532908d778SJames Bottomley  * are actually enabled (enabled by default, max number of phys
7542908d778SJames Bottomley  * enableable in this case).
7552908d778SJames Bottomley  */
asd_ms_get_phy_params(struct asd_ha_struct * asd_ha,struct asd_manuf_sec * manuf_sec)7562908d778SJames Bottomley static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
7572908d778SJames Bottomley 				 struct asd_manuf_sec *manuf_sec)
7582908d778SJames Bottomley {
7592908d778SJames Bottomley 	int i;
7602908d778SJames Bottomley 	int en_phys = 0;
7612908d778SJames Bottomley 	int rep_phys = 0;
7622908d778SJames Bottomley 	struct asd_manuf_phy_param *phy_param;
7632908d778SJames Bottomley 	struct asd_manuf_phy_param dflt_phy_param;
7642908d778SJames Bottomley 
7652908d778SJames Bottomley 	phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M');
7662908d778SJames Bottomley 	if (!phy_param) {
7672908d778SJames Bottomley 		ASD_DPRINTK("ms: no phy parameters found\n");
7682908d778SJames Bottomley 		ASD_DPRINTK("ms: Creating default phy parameters\n");
7692908d778SJames Bottomley 		dflt_phy_param.sig[0] = 'P';
7702908d778SJames Bottomley 		dflt_phy_param.sig[1] = 'M';
7712908d778SJames Bottomley 		dflt_phy_param.maj = 0;
7722908d778SJames Bottomley 		dflt_phy_param.min = 2;
7732908d778SJames Bottomley 		dflt_phy_param.num_phy_desc = 8;
7742908d778SJames Bottomley 		dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc);
7752908d778SJames Bottomley 		for (i =0; i < ASD_MAX_PHYS; i++) {
7762908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].state = 0;
7772908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_id = i;
7782908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6;
7792908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_1 = 0x10;
7802908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_2 = 0x43;
7812908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb;
7822908d778SJames Bottomley 		}
7832908d778SJames Bottomley 
7842908d778SJames Bottomley 		phy_param = &dflt_phy_param;
7852908d778SJames Bottomley 
7862908d778SJames Bottomley 	}
7872908d778SJames Bottomley 
7882908d778SJames Bottomley 	if (phy_param->maj != 0) {
7892908d778SJames Bottomley 		asd_printk("unsupported manuf. phy param major version:0x%x\n",
7902908d778SJames Bottomley 			   phy_param->maj);
7912908d778SJames Bottomley 		return -ENOENT;
7922908d778SJames Bottomley 	}
7932908d778SJames Bottomley 
7942908d778SJames Bottomley 	ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc);
7952908d778SJames Bottomley 	asd_ha->hw_prof.enabled_phys = 0;
7962908d778SJames Bottomley 	for (i = 0; i < phy_param->num_phy_desc; i++) {
7972908d778SJames Bottomley 		struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i];
7982908d778SJames Bottomley 		switch (pd->state & 0xF) {
7992908d778SJames Bottomley 		case MS_PHY_STATE_HIDDEN:
8002908d778SJames Bottomley 			ASD_DPRINTK("ms: phy%d: HIDDEN\n", i);
8012908d778SJames Bottomley 			continue;
8022908d778SJames Bottomley 		case MS_PHY_STATE_REPORTED:
8032908d778SJames Bottomley 			ASD_DPRINTK("ms: phy%d: REPORTED\n", i);
8042908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
8052908d778SJames Bottomley 			rep_phys++;
8062908d778SJames Bottomley 			continue;
80786b9c4c1SAlexis Bruemmer 		case MS_PHY_STATE_ENABLED:
80886b9c4c1SAlexis Bruemmer 			ASD_DPRINTK("ms: phy%d: ENABLED\n", i);
8092908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys |= (1 << i);
8102908d778SJames Bottomley 			en_phys++;
8112908d778SJames Bottomley 			break;
8122908d778SJames Bottomley 		}
8132908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0;
8142908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1;
8152908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2;
8162908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3;
8172908d778SJames Bottomley 	}
8182908d778SJames Bottomley 	asd_ha->hw_prof.max_phys = rep_phys + en_phys;
8192908d778SJames Bottomley 	asd_ha->hw_prof.num_phys = en_phys;
8202908d778SJames Bottomley 	ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n",
8212908d778SJames Bottomley 		    asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys);
8222908d778SJames Bottomley 	ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys);
8232908d778SJames Bottomley 	return 0;
8242908d778SJames Bottomley }
8252908d778SJames Bottomley 
asd_ms_get_connector_map(struct asd_ha_struct * asd_ha,struct asd_manuf_sec * manuf_sec)8262908d778SJames Bottomley static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha,
8272908d778SJames Bottomley 				    struct asd_manuf_sec *manuf_sec)
8282908d778SJames Bottomley {
8292908d778SJames Bottomley 	struct asd_ms_conn_map *cm;
8302908d778SJames Bottomley 
8312908d778SJames Bottomley 	cm = asd_find_ll_by_id(manuf_sec, 'M', 'C');
8322908d778SJames Bottomley 	if (!cm) {
8332908d778SJames Bottomley 		ASD_DPRINTK("ms: no connector map found\n");
8342908d778SJames Bottomley 		return 0;
8352908d778SJames Bottomley 	}
8362908d778SJames Bottomley 
8372908d778SJames Bottomley 	if (cm->maj != 0) {
8382908d778SJames Bottomley 		ASD_DPRINTK("ms: unsupported: connector map major version 0x%x"
8392908d778SJames Bottomley 			    "\n", cm->maj);
8402908d778SJames Bottomley 		return -ENOENT;
8412908d778SJames Bottomley 	}
8422908d778SJames Bottomley 
8432908d778SJames Bottomley 	/* XXX */
8442908d778SJames Bottomley 
8452908d778SJames Bottomley 	return 0;
8462908d778SJames Bottomley }
8472908d778SJames Bottomley 
8482908d778SJames Bottomley 
8492908d778SJames Bottomley /**
8502908d778SJames Bottomley  * asd_process_ms - find and extract information from the manufacturing sector
8512908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
8522908d778SJames Bottomley  * @flash_dir: pointer to the flash directory
8532908d778SJames Bottomley  */
asd_process_ms(struct asd_ha_struct * asd_ha,struct asd_flash_dir * flash_dir)8542908d778SJames Bottomley static int asd_process_ms(struct asd_ha_struct *asd_ha,
8552908d778SJames Bottomley 			  struct asd_flash_dir *flash_dir)
8562908d778SJames Bottomley {
8572908d778SJames Bottomley 	int err;
8582908d778SJames Bottomley 	struct asd_manuf_sec *manuf_sec;
8592908d778SJames Bottomley 	u32 offs, size;
8602908d778SJames Bottomley 
8612908d778SJames Bottomley 	err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size);
8622908d778SJames Bottomley 	if (err) {
8632908d778SJames Bottomley 		ASD_DPRINTK("Couldn't find the manuf. sector\n");
8642908d778SJames Bottomley 		goto out;
8652908d778SJames Bottomley 	}
8662908d778SJames Bottomley 
8672908d778SJames Bottomley 	if (size == 0)
8682908d778SJames Bottomley 		goto out;
8692908d778SJames Bottomley 
8702908d778SJames Bottomley 	err = -ENOMEM;
8712908d778SJames Bottomley 	manuf_sec = kmalloc(size, GFP_KERNEL);
8722908d778SJames Bottomley 	if (!manuf_sec) {
8732908d778SJames Bottomley 		ASD_DPRINTK("no mem for manuf sector\n");
8742908d778SJames Bottomley 		goto out;
8752908d778SJames Bottomley 	}
8762908d778SJames Bottomley 
8772908d778SJames Bottomley 	err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size);
8782908d778SJames Bottomley 	if (err) {
8792908d778SJames Bottomley 		ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n",
8802908d778SJames Bottomley 			    offs, size);
8812908d778SJames Bottomley 		goto out2;
8822908d778SJames Bottomley 	}
8832908d778SJames Bottomley 
8842908d778SJames Bottomley 	err = asd_validate_ms(manuf_sec);
8852908d778SJames Bottomley 	if (err) {
8862908d778SJames Bottomley 		ASD_DPRINTK("couldn't validate manuf sector\n");
8872908d778SJames Bottomley 		goto out2;
8882908d778SJames Bottomley 	}
8892908d778SJames Bottomley 
8902908d778SJames Bottomley 	err = asd_ms_get_sas_addr(asd_ha, manuf_sec);
8912908d778SJames Bottomley 	if (err) {
8922908d778SJames Bottomley 		ASD_DPRINTK("couldn't read the SAS_ADDR\n");
8932908d778SJames Bottomley 		goto out2;
8942908d778SJames Bottomley 	}
8952908d778SJames Bottomley 	ASD_DPRINTK("manuf sect SAS_ADDR %llx\n",
8962908d778SJames Bottomley 		    SAS_ADDR(asd_ha->hw_prof.sas_addr));
8972908d778SJames Bottomley 
8982908d778SJames Bottomley 	err = asd_ms_get_pcba_sn(asd_ha, manuf_sec);
8992908d778SJames Bottomley 	if (err) {
9002908d778SJames Bottomley 		ASD_DPRINTK("couldn't read the PCBA SN\n");
9012908d778SJames Bottomley 		goto out2;
9022908d778SJames Bottomley 	}
9032908d778SJames Bottomley 	ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn);
9042908d778SJames Bottomley 
9052908d778SJames Bottomley 	err = asd_ms_get_phy_params(asd_ha, manuf_sec);
9062908d778SJames Bottomley 	if (err) {
9072908d778SJames Bottomley 		ASD_DPRINTK("ms: couldn't get phy parameters\n");
9082908d778SJames Bottomley 		goto out2;
9092908d778SJames Bottomley 	}
9102908d778SJames Bottomley 
9112908d778SJames Bottomley 	err = asd_ms_get_connector_map(asd_ha, manuf_sec);
9122908d778SJames Bottomley 	if (err) {
9132908d778SJames Bottomley 		ASD_DPRINTK("ms: couldn't get connector map\n");
9142908d778SJames Bottomley 		goto out2;
9152908d778SJames Bottomley 	}
9162908d778SJames Bottomley 
9172908d778SJames Bottomley out2:
9182908d778SJames Bottomley 	kfree(manuf_sec);
9192908d778SJames Bottomley out:
9202908d778SJames Bottomley 	return err;
9212908d778SJames Bottomley }
9222908d778SJames Bottomley 
asd_process_ctrla_phy_settings(struct asd_ha_struct * asd_ha,struct asd_ctrla_phy_settings * ps)9232908d778SJames Bottomley static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha,
9242908d778SJames Bottomley 					  struct asd_ctrla_phy_settings *ps)
9252908d778SJames Bottomley {
9262908d778SJames Bottomley 	int i;
9272908d778SJames Bottomley 	for (i = 0; i < ps->num_phys; i++) {
9282908d778SJames Bottomley 		struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i];
9292908d778SJames Bottomley 
9302908d778SJames Bottomley 		if (!PHY_ENABLED(asd_ha, i))
9312908d778SJames Bottomley 			continue;
9322908d778SJames Bottomley 		if (*(u64 *)pe->sas_addr == 0) {
9332908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
9342908d778SJames Bottomley 			continue;
9352908d778SJames Bottomley 		}
9362908d778SJames Bottomley 		/* This is the SAS address which should be sent in IDENTIFY. */
9372908d778SJames Bottomley 		memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr,
9382908d778SJames Bottomley 		       SAS_ADDR_SIZE);
9392908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].max_sas_lrate =
9402908d778SJames Bottomley 			(pe->sas_link_rates & 0xF0) >> 4;
9412908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].min_sas_lrate =
9422908d778SJames Bottomley 			(pe->sas_link_rates & 0x0F);
9432908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].max_sata_lrate =
9442908d778SJames Bottomley 			(pe->sata_link_rates & 0xF0) >> 4;
9452908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].min_sata_lrate =
9462908d778SJames Bottomley 			(pe->sata_link_rates & 0x0F);
9472908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].flags = pe->flags;
9482908d778SJames Bottomley 		ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x,"
9492908d778SJames Bottomley 			    " sata rate:0x%x-0x%x, flags:0x%x\n",
9502908d778SJames Bottomley 			    i,
9512908d778SJames Bottomley 			    SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr),
9522908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].max_sas_lrate,
9532908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].min_sas_lrate,
9542908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].max_sata_lrate,
9552908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].min_sata_lrate,
9562908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].flags);
9572908d778SJames Bottomley 	}
9582908d778SJames Bottomley 
9592908d778SJames Bottomley 	return 0;
9602908d778SJames Bottomley }
9612908d778SJames Bottomley 
9622908d778SJames Bottomley /**
9632908d778SJames Bottomley  * asd_process_ctrl_a_user - process CTRL-A user settings
9642908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
9652908d778SJames Bottomley  * @flash_dir: pointer to the flash directory
9662908d778SJames Bottomley  */
asd_process_ctrl_a_user(struct asd_ha_struct * asd_ha,struct asd_flash_dir * flash_dir)9672908d778SJames Bottomley static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
9682908d778SJames Bottomley 				   struct asd_flash_dir *flash_dir)
9692908d778SJames Bottomley {
9702908d778SJames Bottomley 	int err, i;
9712908d778SJames Bottomley 	u32 offs, size;
97236dd5acdSHannes Reinecke 	struct asd_ll_el *el = NULL;
9732908d778SJames Bottomley 	struct asd_ctrla_phy_settings *ps;
9742908d778SJames Bottomley 	struct asd_ctrla_phy_settings dflt_ps;
9752908d778SJames Bottomley 
9762908d778SJames Bottomley 	err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size);
9772908d778SJames Bottomley 	if (err) {
9782908d778SJames Bottomley 		ASD_DPRINTK("couldn't find CTRL-A user settings section\n");
9792908d778SJames Bottomley 		ASD_DPRINTK("Creating default CTRL-A user settings section\n");
9802908d778SJames Bottomley 
9812908d778SJames Bottomley 		dflt_ps.id0 = 'h';
9822908d778SJames Bottomley 		dflt_ps.num_phys = 8;
9832908d778SJames Bottomley 		for (i =0; i < ASD_MAX_PHYS; i++) {
9842908d778SJames Bottomley 			memcpy(dflt_ps.phy_ent[i].sas_addr,
9852908d778SJames Bottomley 			       asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
9862908d778SJames Bottomley 			dflt_ps.phy_ent[i].sas_link_rates = 0x98;
9872908d778SJames Bottomley 			dflt_ps.phy_ent[i].flags = 0x0;
9882908d778SJames Bottomley 			dflt_ps.phy_ent[i].sata_link_rates = 0x0;
9892908d778SJames Bottomley 		}
9902908d778SJames Bottomley 
9912908d778SJames Bottomley 		size = sizeof(struct asd_ctrla_phy_settings);
9922908d778SJames Bottomley 		ps = &dflt_ps;
99336dd5acdSHannes Reinecke 		goto out_process;
9942908d778SJames Bottomley 	}
9952908d778SJames Bottomley 
9962908d778SJames Bottomley 	if (size == 0)
9972908d778SJames Bottomley 		goto out;
9982908d778SJames Bottomley 
9992908d778SJames Bottomley 	err = -ENOMEM;
10002908d778SJames Bottomley 	el = kmalloc(size, GFP_KERNEL);
10012908d778SJames Bottomley 	if (!el) {
10022908d778SJames Bottomley 		ASD_DPRINTK("no mem for ctrla user settings section\n");
10032908d778SJames Bottomley 		goto out;
10042908d778SJames Bottomley 	}
10052908d778SJames Bottomley 
10062908d778SJames Bottomley 	err = asd_read_flash_seg(asd_ha, (void *)el, offs, size);
10072908d778SJames Bottomley 	if (err) {
10082908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ctrla phy settings section\n");
10092908d778SJames Bottomley 		goto out2;
10102908d778SJames Bottomley 	}
10112908d778SJames Bottomley 
10122908d778SJames Bottomley 	err = -ENOENT;
10132908d778SJames Bottomley 	ps = asd_find_ll_by_id(el, 'h', 0xFF);
10142908d778SJames Bottomley 	if (!ps) {
10152908d778SJames Bottomley 		ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
10162908d778SJames Bottomley 		goto out2;
10172908d778SJames Bottomley 	}
101836dd5acdSHannes Reinecke out_process:
10192908d778SJames Bottomley 	err = asd_process_ctrla_phy_settings(asd_ha, ps);
10202908d778SJames Bottomley 	if (err) {
10212908d778SJames Bottomley 		ASD_DPRINTK("couldn't process ctrla phy settings\n");
10222908d778SJames Bottomley 		goto out2;
10232908d778SJames Bottomley 	}
10242908d778SJames Bottomley out2:
10252908d778SJames Bottomley 	kfree(el);
10262908d778SJames Bottomley out:
10272908d778SJames Bottomley 	return err;
10282908d778SJames Bottomley }
10292908d778SJames Bottomley 
10302908d778SJames Bottomley /**
10312908d778SJames Bottomley  * asd_read_flash - read flash memory
10322908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
10332908d778SJames Bottomley  */
asd_read_flash(struct asd_ha_struct * asd_ha)10342908d778SJames Bottomley int asd_read_flash(struct asd_ha_struct *asd_ha)
10352908d778SJames Bottomley {
10362908d778SJames Bottomley 	int err;
10372908d778SJames Bottomley 	struct asd_flash_dir *flash_dir;
10382908d778SJames Bottomley 
10392908d778SJames Bottomley 	err = asd_flash_getid(asd_ha);
10402908d778SJames Bottomley 	if (err)
10412908d778SJames Bottomley 		return err;
10422908d778SJames Bottomley 
10432908d778SJames Bottomley 	flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL);
10442908d778SJames Bottomley 	if (!flash_dir)
10452908d778SJames Bottomley 		return -ENOMEM;
10462908d778SJames Bottomley 
10472908d778SJames Bottomley 	err = -ENOENT;
10482908d778SJames Bottomley 	if (!asd_find_flash_dir(asd_ha, flash_dir)) {
10492908d778SJames Bottomley 		ASD_DPRINTK("couldn't find flash directory\n");
10502908d778SJames Bottomley 		goto out;
10512908d778SJames Bottomley 	}
10522908d778SJames Bottomley 
10532908d778SJames Bottomley 	if (le32_to_cpu(flash_dir->rev) != 2) {
10542908d778SJames Bottomley 		asd_printk("unsupported flash dir version:0x%x\n",
10552908d778SJames Bottomley 			   le32_to_cpu(flash_dir->rev));
10562908d778SJames Bottomley 		goto out;
10572908d778SJames Bottomley 	}
10582908d778SJames Bottomley 
10592908d778SJames Bottomley 	err = asd_process_ms(asd_ha, flash_dir);
10602908d778SJames Bottomley 	if (err) {
10612908d778SJames Bottomley 		ASD_DPRINTK("couldn't process manuf sector settings\n");
10622908d778SJames Bottomley 		goto out;
10632908d778SJames Bottomley 	}
10642908d778SJames Bottomley 
10652908d778SJames Bottomley 	err = asd_process_ctrl_a_user(asd_ha, flash_dir);
10662908d778SJames Bottomley 	if (err) {
10672908d778SJames Bottomley 		ASD_DPRINTK("couldn't process CTRL-A user settings\n");
10682908d778SJames Bottomley 		goto out;
10692908d778SJames Bottomley 	}
10702908d778SJames Bottomley 
10712908d778SJames Bottomley out:
10722908d778SJames Bottomley 	kfree(flash_dir);
10732908d778SJames Bottomley 	return err;
10742908d778SJames Bottomley }
10751237c98dSGilbert Wu 
10761237c98dSGilbert Wu /**
10771237c98dSGilbert Wu  * asd_verify_flash_seg - verify data with flash memory
10781237c98dSGilbert Wu  * @asd_ha: pointer to the host adapter structure
10791237c98dSGilbert Wu  * @src: pointer to the source data to be verified
10801237c98dSGilbert Wu  * @dest_offset: offset from flash memory
10811237c98dSGilbert Wu  * @bytes_to_verify: total bytes to verify
10821237c98dSGilbert Wu  */
asd_verify_flash_seg(struct asd_ha_struct * asd_ha,const void * src,u32 dest_offset,u32 bytes_to_verify)10831237c98dSGilbert Wu int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
10840bc202e0SDavid Woodhouse 			 const void *src, u32 dest_offset, u32 bytes_to_verify)
10851237c98dSGilbert Wu {
10860bc202e0SDavid Woodhouse 	const u8 *src_buf;
10871237c98dSGilbert Wu 	u8 flash_char;
10881237c98dSGilbert Wu 	int err;
10891237c98dSGilbert Wu 	u32 nv_offset, reg, i;
10901237c98dSGilbert Wu 
10911237c98dSGilbert Wu 	reg = asd_ha->hw_prof.flash.bar;
10921237c98dSGilbert Wu 	src_buf = NULL;
10931237c98dSGilbert Wu 
10941237c98dSGilbert Wu 	err = FLASH_OK;
10951237c98dSGilbert Wu 	nv_offset = dest_offset;
10960bc202e0SDavid Woodhouse 	src_buf = (const u8 *)src;
10971237c98dSGilbert Wu 	for (i = 0; i < bytes_to_verify; i++) {
10981237c98dSGilbert Wu 		flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i);
10991237c98dSGilbert Wu 		if (flash_char != src_buf[i]) {
11001237c98dSGilbert Wu 			err = FAIL_VERIFY;
11011237c98dSGilbert Wu 			break;
11021237c98dSGilbert Wu 		}
11031237c98dSGilbert Wu 	}
11041237c98dSGilbert Wu 	return err;
11051237c98dSGilbert Wu }
11061237c98dSGilbert Wu 
11071237c98dSGilbert Wu /**
11081237c98dSGilbert Wu  * asd_write_flash_seg - write data into flash memory
11091237c98dSGilbert Wu  * @asd_ha: pointer to the host adapter structure
11101237c98dSGilbert Wu  * @src: pointer to the source data to be written
11111237c98dSGilbert Wu  * @dest_offset: offset from flash memory
11121237c98dSGilbert Wu  * @bytes_to_write: total bytes to write
11131237c98dSGilbert Wu  */
asd_write_flash_seg(struct asd_ha_struct * asd_ha,const void * src,u32 dest_offset,u32 bytes_to_write)11141237c98dSGilbert Wu int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
11150bc202e0SDavid Woodhouse 			const void *src, u32 dest_offset, u32 bytes_to_write)
11161237c98dSGilbert Wu {
11170bc202e0SDavid Woodhouse 	const u8 *src_buf;
11181237c98dSGilbert Wu 	u32 nv_offset, reg, i;
11191237c98dSGilbert Wu 	int err;
11201237c98dSGilbert Wu 
11211237c98dSGilbert Wu 	reg = asd_ha->hw_prof.flash.bar;
11221237c98dSGilbert Wu 	src_buf = NULL;
11231237c98dSGilbert Wu 
11241237c98dSGilbert Wu 	err = asd_check_flash_type(asd_ha);
11251237c98dSGilbert Wu 	if (err) {
11261237c98dSGilbert Wu 		ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err);
11271237c98dSGilbert Wu 		return err;
11281237c98dSGilbert Wu 	}
11291237c98dSGilbert Wu 
11301237c98dSGilbert Wu 	nv_offset = dest_offset;
11311237c98dSGilbert Wu 	err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write);
11321237c98dSGilbert Wu 	if (err) {
11331237c98dSGilbert Wu 		ASD_DPRINTK("Erase failed at offset:0x%x\n",
11341237c98dSGilbert Wu 			nv_offset);
11351237c98dSGilbert Wu 		return err;
11361237c98dSGilbert Wu 	}
11371237c98dSGilbert Wu 
11381237c98dSGilbert Wu 	err = asd_reset_flash(asd_ha);
11391237c98dSGilbert Wu 	if (err) {
11401237c98dSGilbert Wu 		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
11411237c98dSGilbert Wu 		return err;
11421237c98dSGilbert Wu 	}
11431237c98dSGilbert Wu 
11440bc202e0SDavid Woodhouse 	src_buf = (const u8 *)src;
11451237c98dSGilbert Wu 	for (i = 0; i < bytes_to_write; i++) {
11461237c98dSGilbert Wu 		/* Setup program command sequence */
11471237c98dSGilbert Wu 		switch (asd_ha->hw_prof.flash.method) {
11481237c98dSGilbert Wu 		case FLASH_METHOD_A:
11491237c98dSGilbert Wu 		{
11501237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11511237c98dSGilbert Wu 					(reg + 0xAAA), 0xAA);
11521237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11531237c98dSGilbert Wu 					(reg + 0x555), 0x55);
11541237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11551237c98dSGilbert Wu 					(reg + 0xAAA), 0xA0);
11561237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11571237c98dSGilbert Wu 					(reg + nv_offset + i),
11581237c98dSGilbert Wu 					(*(src_buf + i)));
11591237c98dSGilbert Wu 			break;
11601237c98dSGilbert Wu 		}
11611237c98dSGilbert Wu 		case FLASH_METHOD_B:
11621237c98dSGilbert Wu 		{
11631237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11641237c98dSGilbert Wu 					(reg + 0x555), 0xAA);
11651237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11661237c98dSGilbert Wu 					(reg + 0x2AA), 0x55);
11671237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11681237c98dSGilbert Wu 					(reg + 0x555), 0xA0);
11691237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha,
11701237c98dSGilbert Wu 					(reg + nv_offset + i),
11711237c98dSGilbert Wu 					(*(src_buf + i)));
11721237c98dSGilbert Wu 			break;
11731237c98dSGilbert Wu 		}
11741237c98dSGilbert Wu 		default:
11751237c98dSGilbert Wu 			break;
11761237c98dSGilbert Wu 		}
11771237c98dSGilbert Wu 		if (asd_chk_write_status(asd_ha,
11781237c98dSGilbert Wu 				(nv_offset + i), 0) != 0) {
11791237c98dSGilbert Wu 			ASD_DPRINTK("aicx: Write failed at offset:0x%x\n",
11801237c98dSGilbert Wu 				reg + nv_offset + i);
11811237c98dSGilbert Wu 			return FAIL_WRITE_FLASH;
11821237c98dSGilbert Wu 		}
11831237c98dSGilbert Wu 	}
11841237c98dSGilbert Wu 
11851237c98dSGilbert Wu 	err = asd_reset_flash(asd_ha);
11861237c98dSGilbert Wu 	if (err) {
11871237c98dSGilbert Wu 		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
11881237c98dSGilbert Wu 		return err;
11891237c98dSGilbert Wu 	}
11901237c98dSGilbert Wu 	return 0;
11911237c98dSGilbert Wu }
11921237c98dSGilbert Wu 
asd_chk_write_status(struct asd_ha_struct * asd_ha,u32 sector_addr,u8 erase_flag)11931237c98dSGilbert Wu int asd_chk_write_status(struct asd_ha_struct *asd_ha,
11941237c98dSGilbert Wu 	 u32 sector_addr, u8 erase_flag)
11951237c98dSGilbert Wu {
11961237c98dSGilbert Wu 	u32 reg;
11971237c98dSGilbert Wu 	u32 loop_cnt;
11981237c98dSGilbert Wu 	u8  nv_data1, nv_data2;
11991237c98dSGilbert Wu 	u8  toggle_bit1;
12001237c98dSGilbert Wu 
12011237c98dSGilbert Wu 	/*
12021237c98dSGilbert Wu 	 * Read from DQ2 requires sector address
12031237c98dSGilbert Wu 	 * while it's dont care for DQ6
12041237c98dSGilbert Wu 	 */
12051237c98dSGilbert Wu 	reg = asd_ha->hw_prof.flash.bar;
12061237c98dSGilbert Wu 
12071237c98dSGilbert Wu 	for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) {
12081237c98dSGilbert Wu 		nv_data1 = asd_read_reg_byte(asd_ha, reg);
12091237c98dSGilbert Wu 		nv_data2 = asd_read_reg_byte(asd_ha, reg);
12101237c98dSGilbert Wu 
12111237c98dSGilbert Wu 		toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
12121237c98dSGilbert Wu 				 ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
12131237c98dSGilbert Wu 
12141237c98dSGilbert Wu 		if (toggle_bit1 == 0) {
12151237c98dSGilbert Wu 			return 0;
12161237c98dSGilbert Wu 		} else {
12171237c98dSGilbert Wu 			if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) {
12181237c98dSGilbert Wu 				nv_data1 = asd_read_reg_byte(asd_ha,
12191237c98dSGilbert Wu 								reg);
12201237c98dSGilbert Wu 				nv_data2 = asd_read_reg_byte(asd_ha,
12211237c98dSGilbert Wu 								reg);
12221237c98dSGilbert Wu 				toggle_bit1 =
12231237c98dSGilbert Wu 				((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
12241237c98dSGilbert Wu 				^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
12251237c98dSGilbert Wu 
12261237c98dSGilbert Wu 				if (toggle_bit1 == 0)
12271237c98dSGilbert Wu 					return 0;
12281237c98dSGilbert Wu 			}
12291237c98dSGilbert Wu 		}
12301237c98dSGilbert Wu 
12311237c98dSGilbert Wu 		/*
12321237c98dSGilbert Wu 		 * ERASE is a sector-by-sector operation and requires
12331237c98dSGilbert Wu 		 * more time to finish while WRITE is byte-byte-byte
12341237c98dSGilbert Wu 		 * operation and takes lesser time to finish.
12351237c98dSGilbert Wu 		 *
12361237c98dSGilbert Wu 		 * For some strange reason a reduced ERASE delay gives different
12371237c98dSGilbert Wu 		 * behaviour across different spirit boards. Hence we set
12381237c98dSGilbert Wu 		 * a optimum balance of 50mus for ERASE which works well
12391237c98dSGilbert Wu 		 * across all boards.
12401237c98dSGilbert Wu 		 */
12411237c98dSGilbert Wu 		if (erase_flag) {
12421237c98dSGilbert Wu 			udelay(FLASH_STATUS_ERASE_DELAY_COUNT);
12431237c98dSGilbert Wu 		} else {
12441237c98dSGilbert Wu 			udelay(FLASH_STATUS_WRITE_DELAY_COUNT);
12451237c98dSGilbert Wu 		}
12461237c98dSGilbert Wu 	}
12471237c98dSGilbert Wu 	return -1;
12481237c98dSGilbert Wu }
12491237c98dSGilbert Wu 
12501237c98dSGilbert Wu /**
12513528b9abSLee Jones  * asd_erase_nv_sector - Erase the flash memory sectors.
12521237c98dSGilbert Wu  * @asd_ha: pointer to the host adapter structure
12531237c98dSGilbert Wu  * @flash_addr: pointer to offset from flash memory
12541237c98dSGilbert Wu  * @size: total bytes to erase.
12551237c98dSGilbert Wu  */
asd_erase_nv_sector(struct asd_ha_struct * asd_ha,u32 flash_addr,u32 size)12561237c98dSGilbert Wu int asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size)
12571237c98dSGilbert Wu {
12581237c98dSGilbert Wu 	u32 reg;
12591237c98dSGilbert Wu 	u32 sector_addr;
12601237c98dSGilbert Wu 
12611237c98dSGilbert Wu 	reg = asd_ha->hw_prof.flash.bar;
12621237c98dSGilbert Wu 
12631237c98dSGilbert Wu 	/* sector staring address */
12641237c98dSGilbert Wu 	sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK;
12651237c98dSGilbert Wu 
12661237c98dSGilbert Wu 	/*
12671237c98dSGilbert Wu 	 * Erasing an flash sector needs to be done in six consecutive
12681237c98dSGilbert Wu 	 * write cyles.
12691237c98dSGilbert Wu 	 */
12701237c98dSGilbert Wu 	while (sector_addr < flash_addr+size) {
12711237c98dSGilbert Wu 		switch (asd_ha->hw_prof.flash.method) {
12721237c98dSGilbert Wu 		case FLASH_METHOD_A:
12731237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
12741237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
12751237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80);
12761237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
12771237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
12781237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
12791237c98dSGilbert Wu 			break;
12801237c98dSGilbert Wu 		case FLASH_METHOD_B:
12811237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
12821237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
12831237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80);
12841237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
12851237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
12861237c98dSGilbert Wu 			asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
12871237c98dSGilbert Wu 			break;
12881237c98dSGilbert Wu 		default:
12891237c98dSGilbert Wu 			break;
12901237c98dSGilbert Wu 		}
12911237c98dSGilbert Wu 
12921237c98dSGilbert Wu 		if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0)
12931237c98dSGilbert Wu 			return FAIL_ERASE_FLASH;
12941237c98dSGilbert Wu 
12951237c98dSGilbert Wu 		sector_addr += FLASH_SECTOR_SIZE;
12961237c98dSGilbert Wu 	}
12971237c98dSGilbert Wu 
12981237c98dSGilbert Wu 	return 0;
12991237c98dSGilbert Wu }
13001237c98dSGilbert Wu 
asd_check_flash_type(struct asd_ha_struct * asd_ha)13011237c98dSGilbert Wu int asd_check_flash_type(struct asd_ha_struct *asd_ha)
13021237c98dSGilbert Wu {
13031237c98dSGilbert Wu 	u8 manuf_id;
13041237c98dSGilbert Wu 	u8 dev_id;
13051237c98dSGilbert Wu 	u8 sec_prot;
13061237c98dSGilbert Wu 	u32 inc;
13071237c98dSGilbert Wu 	u32 reg;
13081237c98dSGilbert Wu 	int err;
13091237c98dSGilbert Wu 
13101237c98dSGilbert Wu 	/* get Flash memory base address */
13111237c98dSGilbert Wu 	reg = asd_ha->hw_prof.flash.bar;
13121237c98dSGilbert Wu 
13131237c98dSGilbert Wu 	/* Determine flash info */
13141237c98dSGilbert Wu 	err = asd_reset_flash(asd_ha);
13151237c98dSGilbert Wu 	if (err) {
13161237c98dSGilbert Wu 		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
13171237c98dSGilbert Wu 		return err;
13181237c98dSGilbert Wu 	}
13191237c98dSGilbert Wu 
13201237c98dSGilbert Wu 	asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN;
13211237c98dSGilbert Wu 	asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN;
13221237c98dSGilbert Wu 	asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN;
13231237c98dSGilbert Wu 
13241237c98dSGilbert Wu 	/* Get flash info. This would most likely be AMD Am29LV family flash.
13251237c98dSGilbert Wu 	 * First try the sequence for word mode.  It is the same as for
13261237c98dSGilbert Wu 	 * 008B (byte mode only), 160B (word mode) and 800D (word mode).
13271237c98dSGilbert Wu 	 */
13281237c98dSGilbert Wu 	inc = asd_ha->hw_prof.flash.wide ? 2 : 1;
13291237c98dSGilbert Wu 	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA);
13301237c98dSGilbert Wu 	asd_write_reg_byte(asd_ha, reg + 0x555, 0x55);
13311237c98dSGilbert Wu 	asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90);
13321237c98dSGilbert Wu 	manuf_id = asd_read_reg_byte(asd_ha, reg);
13331237c98dSGilbert Wu 	dev_id = asd_read_reg_byte(asd_ha, reg + inc);
13341237c98dSGilbert Wu 	sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
13351237c98dSGilbert Wu 	/* Get out of autoselect mode. */
13361237c98dSGilbert Wu 	err = asd_reset_flash(asd_ha);
13371237c98dSGilbert Wu 	if (err) {
13381237c98dSGilbert Wu 		ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
13391237c98dSGilbert Wu 		return err;
13401237c98dSGilbert Wu 	}
13411237c98dSGilbert Wu 	ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) "
13421237c98dSGilbert Wu 		"sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot);
13431237c98dSGilbert Wu 	err = asd_reset_flash(asd_ha);
13441237c98dSGilbert Wu 	if (err != 0)
13451237c98dSGilbert Wu 		return err;
13461237c98dSGilbert Wu 
13471237c98dSGilbert Wu 	switch (manuf_id) {
13481237c98dSGilbert Wu 	case FLASH_MANUF_ID_AMD:
13491237c98dSGilbert Wu 		switch (sec_prot) {
13501237c98dSGilbert Wu 		case FLASH_DEV_ID_AM29LV800DT:
13511237c98dSGilbert Wu 		case FLASH_DEV_ID_AM29LV640MT:
13521237c98dSGilbert Wu 		case FLASH_DEV_ID_AM29F800B:
13531237c98dSGilbert Wu 			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
13541237c98dSGilbert Wu 			break;
13551237c98dSGilbert Wu 		default:
13561237c98dSGilbert Wu 			break;
13571237c98dSGilbert Wu 		}
13581237c98dSGilbert Wu 		break;
13591237c98dSGilbert Wu 	case FLASH_MANUF_ID_ST:
13601237c98dSGilbert Wu 		switch (sec_prot) {
13611237c98dSGilbert Wu 		case FLASH_DEV_ID_STM29W800DT:
13621237c98dSGilbert Wu 		case FLASH_DEV_ID_STM29LV640:
13631237c98dSGilbert Wu 			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
13641237c98dSGilbert Wu 			break;
13651237c98dSGilbert Wu 		default:
13661237c98dSGilbert Wu 			break;
13671237c98dSGilbert Wu 		}
13681237c98dSGilbert Wu 		break;
13691237c98dSGilbert Wu 	case FLASH_MANUF_ID_FUJITSU:
13701237c98dSGilbert Wu 		switch (sec_prot) {
13711237c98dSGilbert Wu 		case FLASH_DEV_ID_MBM29LV800TE:
13721237c98dSGilbert Wu 		case FLASH_DEV_ID_MBM29DL800TA:
13731237c98dSGilbert Wu 			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
13741237c98dSGilbert Wu 			break;
13751237c98dSGilbert Wu 		}
13761237c98dSGilbert Wu 		break;
13771237c98dSGilbert Wu 	case FLASH_MANUF_ID_MACRONIX:
13781237c98dSGilbert Wu 		switch (sec_prot) {
13791237c98dSGilbert Wu 		case FLASH_DEV_ID_MX29LV800BT:
13801237c98dSGilbert Wu 			asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
13811237c98dSGilbert Wu 			break;
13821237c98dSGilbert Wu 		}
13831237c98dSGilbert Wu 		break;
13841237c98dSGilbert Wu 	}
13851237c98dSGilbert Wu 
13861237c98dSGilbert Wu 	if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) {
13871237c98dSGilbert Wu 		err = asd_reset_flash(asd_ha);
13881237c98dSGilbert Wu 		if (err) {
13891237c98dSGilbert Wu 			ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
13901237c98dSGilbert Wu 			return err;
13911237c98dSGilbert Wu 		}
13921237c98dSGilbert Wu 
13931237c98dSGilbert Wu 		/* Issue Unlock sequence for AM29LV008BT */
13941237c98dSGilbert Wu 		asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
13951237c98dSGilbert Wu 		asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
13961237c98dSGilbert Wu 		asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90);
13971237c98dSGilbert Wu 		manuf_id = asd_read_reg_byte(asd_ha, reg);
13981237c98dSGilbert Wu 		dev_id = asd_read_reg_byte(asd_ha, reg + inc);
13991237c98dSGilbert Wu 		sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
14001237c98dSGilbert Wu 
14011237c98dSGilbert Wu 		ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot"
14021237c98dSGilbert Wu 			"(0x%x)\n", manuf_id, dev_id, sec_prot);
14031237c98dSGilbert Wu 
14041237c98dSGilbert Wu 		err = asd_reset_flash(asd_ha);
14051237c98dSGilbert Wu 		if (err != 0) {
14061237c98dSGilbert Wu 			ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
14071237c98dSGilbert Wu 			return err;
14081237c98dSGilbert Wu 		}
14091237c98dSGilbert Wu 
14101237c98dSGilbert Wu 		switch (manuf_id) {
14111237c98dSGilbert Wu 		case FLASH_MANUF_ID_AMD:
14121237c98dSGilbert Wu 			switch (dev_id) {
14131237c98dSGilbert Wu 			case FLASH_DEV_ID_AM29LV008BT:
14141237c98dSGilbert Wu 				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
14151237c98dSGilbert Wu 				break;
14161237c98dSGilbert Wu 			default:
14171237c98dSGilbert Wu 				break;
14181237c98dSGilbert Wu 			}
14191237c98dSGilbert Wu 			break;
14201237c98dSGilbert Wu 		case FLASH_MANUF_ID_ST:
14211237c98dSGilbert Wu 			switch (dev_id) {
14221237c98dSGilbert Wu 			case FLASH_DEV_ID_STM29008:
14231237c98dSGilbert Wu 				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
14241237c98dSGilbert Wu 				break;
14251237c98dSGilbert Wu 			default:
14261237c98dSGilbert Wu 				break;
14271237c98dSGilbert Wu 			}
14281237c98dSGilbert Wu 			break;
14291237c98dSGilbert Wu 		case FLASH_MANUF_ID_FUJITSU:
14301237c98dSGilbert Wu 			switch (dev_id) {
14311237c98dSGilbert Wu 			case FLASH_DEV_ID_MBM29LV008TA:
14321237c98dSGilbert Wu 				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
14331237c98dSGilbert Wu 				break;
14341237c98dSGilbert Wu 			}
14351237c98dSGilbert Wu 			break;
14361237c98dSGilbert Wu 		case FLASH_MANUF_ID_INTEL:
14371237c98dSGilbert Wu 			switch (dev_id) {
14381237c98dSGilbert Wu 			case FLASH_DEV_ID_I28LV00TAT:
14391237c98dSGilbert Wu 				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
14401237c98dSGilbert Wu 				break;
14411237c98dSGilbert Wu 			}
14421237c98dSGilbert Wu 			break;
14431237c98dSGilbert Wu 		case FLASH_MANUF_ID_MACRONIX:
14441237c98dSGilbert Wu 			switch (dev_id) {
14451237c98dSGilbert Wu 			case FLASH_DEV_ID_I28LV00TAT:
14461237c98dSGilbert Wu 				asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
14471237c98dSGilbert Wu 				break;
14481237c98dSGilbert Wu 			}
14491237c98dSGilbert Wu 			break;
14501237c98dSGilbert Wu 		default:
14511237c98dSGilbert Wu 			return FAIL_FIND_FLASH_ID;
14521237c98dSGilbert Wu 		}
14531237c98dSGilbert Wu 	}
14541237c98dSGilbert Wu 
14551237c98dSGilbert Wu 	if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN)
14561237c98dSGilbert Wu 	      return FAIL_FIND_FLASH_ID;
14571237c98dSGilbert Wu 
14581237c98dSGilbert Wu 	asd_ha->hw_prof.flash.manuf = manuf_id;
14591237c98dSGilbert Wu 	asd_ha->hw_prof.flash.dev_id = dev_id;
14601237c98dSGilbert Wu 	asd_ha->hw_prof.flash.sec_prot = sec_prot;
14611237c98dSGilbert Wu 	return 0;
14621237c98dSGilbert Wu }
1463