12908d778SJames Bottomley /*
22908d778SJames Bottomley  * Aic94xx SAS/SATA driver access to shared data structures and memory
32908d778SJames Bottomley  * maps.
42908d778SJames Bottomley  *
52908d778SJames Bottomley  * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
62908d778SJames Bottomley  * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
72908d778SJames Bottomley  *
82908d778SJames Bottomley  * This file is licensed under GPLv2.
92908d778SJames Bottomley  *
102908d778SJames Bottomley  * This file is part of the aic94xx driver.
112908d778SJames Bottomley  *
122908d778SJames Bottomley  * The aic94xx driver is free software; you can redistribute it and/or
132908d778SJames Bottomley  * modify it under the terms of the GNU General Public License as
142908d778SJames Bottomley  * published by the Free Software Foundation; version 2 of the
152908d778SJames Bottomley  * License.
162908d778SJames Bottomley  *
172908d778SJames Bottomley  * The aic94xx driver is distributed in the hope that it will be useful,
182908d778SJames Bottomley  * but WITHOUT ANY WARRANTY; without even the implied warranty of
192908d778SJames Bottomley  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
202908d778SJames Bottomley  * General Public License for more details.
212908d778SJames Bottomley  *
222908d778SJames Bottomley  * You should have received a copy of the GNU General Public License
232908d778SJames Bottomley  * along with the aic94xx driver; if not, write to the Free Software
242908d778SJames Bottomley  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
252908d778SJames Bottomley  *
262908d778SJames Bottomley  */
272908d778SJames Bottomley 
282908d778SJames Bottomley #include <linux/pci.h>
292908d778SJames Bottomley #include <linux/delay.h>
302908d778SJames Bottomley 
312908d778SJames Bottomley #include "aic94xx.h"
322908d778SJames Bottomley #include "aic94xx_reg.h"
332908d778SJames Bottomley 
342908d778SJames Bottomley /* ---------- OCM stuff ---------- */
352908d778SJames Bottomley 
362908d778SJames Bottomley struct asd_ocm_dir_ent {
372908d778SJames Bottomley 	u8 type;
382908d778SJames Bottomley 	u8 offs[3];
392908d778SJames Bottomley 	u8 _r1;
402908d778SJames Bottomley 	u8 size[3];
412908d778SJames Bottomley } __attribute__ ((packed));
422908d778SJames Bottomley 
432908d778SJames Bottomley struct asd_ocm_dir {
442908d778SJames Bottomley 	char sig[2];
452908d778SJames Bottomley 	u8   _r1[2];
462908d778SJames Bottomley 	u8   major;          /* 0 */
472908d778SJames Bottomley 	u8   minor;          /* 0 */
482908d778SJames Bottomley 	u8   _r2;
492908d778SJames Bottomley 	u8   num_de;
502908d778SJames Bottomley 	struct asd_ocm_dir_ent entry[15];
512908d778SJames Bottomley } __attribute__ ((packed));
522908d778SJames Bottomley 
532908d778SJames Bottomley #define	OCM_DE_OCM_DIR			0x00
542908d778SJames Bottomley #define	OCM_DE_WIN_DRVR			0x01
552908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM		0x02
562908d778SJames Bottomley #define	OCM_DE_RAID_ENGN		0x03
572908d778SJames Bottomley #define	OCM_DE_BIOS_INTL		0x04
582908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM_OSM		0x05
592908d778SJames Bottomley #define	OCM_DE_BIOS_CHIM_DYNAMIC	0x06
602908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES0		0x07
612908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES1		0x08
622908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES2		0x09
632908d778SJames Bottomley #define	OCM_DE_ADDC2C_RES3		0x0A
642908d778SJames Bottomley 
652908d778SJames Bottomley #define OCM_INIT_DIR_ENTRIES	5
662908d778SJames Bottomley /***************************************************************************
670779bf2dSMatt LaPlante *  OCM directory default
682908d778SJames Bottomley ***************************************************************************/
692908d778SJames Bottomley static struct asd_ocm_dir OCMDirInit =
702908d778SJames Bottomley {
712908d778SJames Bottomley 	.sig = {0x4D, 0x4F},	/* signature */
722908d778SJames Bottomley 	.num_de = OCM_INIT_DIR_ENTRIES,	/* no. of directory entries */
732908d778SJames Bottomley };
742908d778SJames Bottomley 
752908d778SJames Bottomley /***************************************************************************
760779bf2dSMatt LaPlante *  OCM directory Entries default
772908d778SJames Bottomley ***************************************************************************/
782908d778SJames Bottomley static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] =
792908d778SJames Bottomley {
802908d778SJames Bottomley 	{
812908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES0),	/* Entry type  */
822908d778SJames Bottomley 		.offs = {128},			/* Offset */
832908d778SJames Bottomley 		.size = {0, 4},			/* size */
842908d778SJames Bottomley 	},
852908d778SJames Bottomley 	{
862908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES1),	/* Entry type  */
872908d778SJames Bottomley 		.offs = {128, 4},		/* Offset */
882908d778SJames Bottomley 		.size = {0, 4},			/* size */
892908d778SJames Bottomley 	},
902908d778SJames Bottomley 	{
912908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES2),	/* Entry type  */
922908d778SJames Bottomley 		.offs = {128, 8},		/* Offset */
932908d778SJames Bottomley 		.size = {0, 4},			/* size */
942908d778SJames Bottomley 	},
952908d778SJames Bottomley 	{
962908d778SJames Bottomley 		.type = (OCM_DE_ADDC2C_RES3),	/* Entry type  */
972908d778SJames Bottomley 		.offs = {128, 12},		/* Offset */
982908d778SJames Bottomley 		.size = {0, 4},			/* size */
992908d778SJames Bottomley 	},
1002908d778SJames Bottomley 	{
1012908d778SJames Bottomley 		.type = (OCM_DE_WIN_DRVR),	/* Entry type  */
1022908d778SJames Bottomley 		.offs = {128, 16},		/* Offset */
1032908d778SJames Bottomley 		.size = {128, 235, 1},		/* size */
1042908d778SJames Bottomley 	},
1052908d778SJames Bottomley };
1062908d778SJames Bottomley 
1072908d778SJames Bottomley struct asd_bios_chim_struct {
1082908d778SJames Bottomley 	char sig[4];
1092908d778SJames Bottomley 	u8   major;          /* 1 */
1102908d778SJames Bottomley 	u8   minor;          /* 0 */
1112908d778SJames Bottomley 	u8   bios_major;
1122908d778SJames Bottomley 	u8   bios_minor;
1132908d778SJames Bottomley 	__le32  bios_build;
1142908d778SJames Bottomley 	u8   flags;
1152908d778SJames Bottomley 	u8   pci_slot;
1162908d778SJames Bottomley 	__le16  ue_num;
1172908d778SJames Bottomley 	__le16  ue_size;
1182908d778SJames Bottomley 	u8  _r[14];
1192908d778SJames Bottomley 	/* The unit element array is right here.
1202908d778SJames Bottomley 	 */
1212908d778SJames Bottomley } __attribute__ ((packed));
1222908d778SJames Bottomley 
1232908d778SJames Bottomley /**
1242908d778SJames Bottomley  * asd_read_ocm_seg - read an on chip memory (OCM) segment
1252908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
1262908d778SJames Bottomley  * @buffer: where to write the read data
1272908d778SJames Bottomley  * @offs: offset into OCM where to read from
1282908d778SJames Bottomley  * @size: how many bytes to read
1292908d778SJames Bottomley  *
1302908d778SJames Bottomley  * Return the number of bytes not read. Return 0 on success.
1312908d778SJames Bottomley  */
1322908d778SJames Bottomley static int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
1332908d778SJames Bottomley 			    u32 offs, int size)
1342908d778SJames Bottomley {
1352908d778SJames Bottomley 	u8 *p = buffer;
1362908d778SJames Bottomley 	if (unlikely(asd_ha->iospace))
1372908d778SJames Bottomley 		asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
1382908d778SJames Bottomley 	else {
1392908d778SJames Bottomley 		for ( ; size > 0; size--, offs++, p++)
1402908d778SJames Bottomley 			*p = asd_read_ocm_byte(asd_ha, offs);
1412908d778SJames Bottomley 	}
1422908d778SJames Bottomley 	return size;
1432908d778SJames Bottomley }
1442908d778SJames Bottomley 
1452908d778SJames Bottomley static int asd_read_ocm_dir(struct asd_ha_struct *asd_ha,
1462908d778SJames Bottomley 			    struct asd_ocm_dir *dir, u32 offs)
1472908d778SJames Bottomley {
1482908d778SJames Bottomley 	int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir));
1492908d778SJames Bottomley 	if (err) {
1502908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ocm segment\n");
1512908d778SJames Bottomley 		return err;
1522908d778SJames Bottomley 	}
1532908d778SJames Bottomley 
1542908d778SJames Bottomley 	if (dir->sig[0] != 'M' || dir->sig[1] != 'O') {
1552908d778SJames Bottomley 		ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n",
1562908d778SJames Bottomley 			    dir->sig[0], dir->sig[1]);
1572908d778SJames Bottomley 		return -ENOENT;
1582908d778SJames Bottomley 	}
1592908d778SJames Bottomley 	if (dir->major != 0) {
1602908d778SJames Bottomley 		asd_printk("unsupported major version of ocm dir:0x%x\n",
1612908d778SJames Bottomley 			   dir->major);
1622908d778SJames Bottomley 		return -ENOENT;
1632908d778SJames Bottomley 	}
1642908d778SJames Bottomley 	dir->num_de &= 0xf;
1652908d778SJames Bottomley 	return 0;
1662908d778SJames Bottomley }
1672908d778SJames Bottomley 
1682908d778SJames Bottomley /**
1692908d778SJames Bottomley  * asd_write_ocm_seg - write an on chip memory (OCM) segment
1702908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
1712908d778SJames Bottomley  * @buffer: where to read the write data
1722908d778SJames Bottomley  * @offs: offset into OCM to write to
1732908d778SJames Bottomley  * @size: how many bytes to write
1742908d778SJames Bottomley  *
1752908d778SJames Bottomley  * Return the number of bytes not written. Return 0 on success.
1762908d778SJames Bottomley  */
1772908d778SJames Bottomley static void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer,
1782908d778SJames Bottomley 			    u32 offs, int size)
1792908d778SJames Bottomley {
1802908d778SJames Bottomley 	u8 *p = buffer;
1812908d778SJames Bottomley 	if (unlikely(asd_ha->iospace))
1822908d778SJames Bottomley 		asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size);
1832908d778SJames Bottomley 	else {
1842908d778SJames Bottomley 		for ( ; size > 0; size--, offs++, p++)
1852908d778SJames Bottomley 			asd_write_ocm_byte(asd_ha, offs, *p);
1862908d778SJames Bottomley 	}
1872908d778SJames Bottomley 	return;
1882908d778SJames Bottomley }
1892908d778SJames Bottomley 
1902908d778SJames Bottomley #define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16))
1912908d778SJames Bottomley 
1922908d778SJames Bottomley static int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type,
1932908d778SJames Bottomley 			      u32 *offs, u32 *size)
1942908d778SJames Bottomley {
1952908d778SJames Bottomley 	int i;
1962908d778SJames Bottomley 	struct asd_ocm_dir_ent *ent;
1972908d778SJames Bottomley 
1982908d778SJames Bottomley 	for (i = 0; i < dir->num_de; i++) {
1992908d778SJames Bottomley 		if (dir->entry[i].type == type)
2002908d778SJames Bottomley 			break;
2012908d778SJames Bottomley 	}
2022908d778SJames Bottomley 	if (i >= dir->num_de)
2032908d778SJames Bottomley 		return -ENOENT;
2042908d778SJames Bottomley 	ent = &dir->entry[i];
2052908d778SJames Bottomley 	*offs = (u32) THREE_TO_NUM(ent->offs);
2062908d778SJames Bottomley 	*size = (u32) THREE_TO_NUM(ent->size);
2072908d778SJames Bottomley 	return 0;
2082908d778SJames Bottomley }
2092908d778SJames Bottomley 
2102908d778SJames Bottomley #define OCM_BIOS_CHIM_DE  2
2112908d778SJames Bottomley #define BC_BIOS_PRESENT   1
2122908d778SJames Bottomley 
2132908d778SJames Bottomley static int asd_get_bios_chim(struct asd_ha_struct *asd_ha,
2142908d778SJames Bottomley 			     struct asd_ocm_dir *dir)
2152908d778SJames Bottomley {
2162908d778SJames Bottomley 	int err;
2172908d778SJames Bottomley 	struct asd_bios_chim_struct *bc_struct;
2182908d778SJames Bottomley 	u32 offs, size;
2192908d778SJames Bottomley 
2202908d778SJames Bottomley 	err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size);
2212908d778SJames Bottomley 	if (err) {
2222908d778SJames Bottomley 		ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n");
2232908d778SJames Bottomley 		goto out;
2242908d778SJames Bottomley 	}
2252908d778SJames Bottomley 	err = -ENOMEM;
2262908d778SJames Bottomley 	bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL);
2272908d778SJames Bottomley 	if (!bc_struct) {
2282908d778SJames Bottomley 		asd_printk("no memory for bios_chim struct\n");
2292908d778SJames Bottomley 		goto out;
2302908d778SJames Bottomley 	}
2312908d778SJames Bottomley 	err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs,
2322908d778SJames Bottomley 			       sizeof(*bc_struct));
2332908d778SJames Bottomley 	if (err) {
2342908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ocm segment\n");
2352908d778SJames Bottomley 		goto out2;
2362908d778SJames Bottomley 	}
2372908d778SJames Bottomley 	if (strncmp(bc_struct->sig, "SOIB", 4)
2382908d778SJames Bottomley 	    && strncmp(bc_struct->sig, "IPSA", 4)) {
2392908d778SJames Bottomley 		ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n",
2402908d778SJames Bottomley 			    bc_struct->sig[0], bc_struct->sig[1],
2412908d778SJames Bottomley 			    bc_struct->sig[2], bc_struct->sig[3]);
2422908d778SJames Bottomley 		err = -ENOENT;
2432908d778SJames Bottomley 		goto out2;
2442908d778SJames Bottomley 	}
2452908d778SJames Bottomley 	if (bc_struct->major != 1) {
2462908d778SJames Bottomley 		asd_printk("BIOS_CHIM unsupported major version:0x%x\n",
2472908d778SJames Bottomley 			   bc_struct->major);
2482908d778SJames Bottomley 		err = -ENOENT;
2492908d778SJames Bottomley 		goto out2;
2502908d778SJames Bottomley 	}
2512908d778SJames Bottomley 	if (bc_struct->flags & BC_BIOS_PRESENT) {
2522908d778SJames Bottomley 		asd_ha->hw_prof.bios.present = 1;
2532908d778SJames Bottomley 		asd_ha->hw_prof.bios.maj = bc_struct->bios_major;
2542908d778SJames Bottomley 		asd_ha->hw_prof.bios.min = bc_struct->bios_minor;
2552908d778SJames Bottomley 		asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build);
2562908d778SJames Bottomley 		ASD_DPRINTK("BIOS present (%d,%d), %d\n",
2572908d778SJames Bottomley 			    asd_ha->hw_prof.bios.maj,
2582908d778SJames Bottomley 			    asd_ha->hw_prof.bios.min,
2592908d778SJames Bottomley 			    asd_ha->hw_prof.bios.bld);
2602908d778SJames Bottomley 	}
2612908d778SJames Bottomley 	asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num);
2622908d778SJames Bottomley 	asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size);
2632908d778SJames Bottomley 	ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num,
2642908d778SJames Bottomley 		    asd_ha->hw_prof.ue.size);
2652908d778SJames Bottomley 	size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size;
2662908d778SJames Bottomley 	if (size > 0) {
2672908d778SJames Bottomley 		err = -ENOMEM;
2682908d778SJames Bottomley 		asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL);
2692908d778SJames Bottomley 		if (!asd_ha->hw_prof.ue.area)
2702908d778SJames Bottomley 			goto out2;
2712908d778SJames Bottomley 		err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area,
2722908d778SJames Bottomley 				       offs + sizeof(*bc_struct), size);
2732908d778SJames Bottomley 		if (err) {
2742908d778SJames Bottomley 			kfree(asd_ha->hw_prof.ue.area);
2752908d778SJames Bottomley 			asd_ha->hw_prof.ue.area = NULL;
2762908d778SJames Bottomley 			asd_ha->hw_prof.ue.num  = 0;
2772908d778SJames Bottomley 			asd_ha->hw_prof.ue.size = 0;
2782908d778SJames Bottomley 			ASD_DPRINTK("couldn't read ue entries(%d)\n", err);
2792908d778SJames Bottomley 		}
2802908d778SJames Bottomley 	}
2812908d778SJames Bottomley out2:
2822908d778SJames Bottomley 	kfree(bc_struct);
2832908d778SJames Bottomley out:
2842908d778SJames Bottomley 	return err;
2852908d778SJames Bottomley }
2862908d778SJames Bottomley 
2872908d778SJames Bottomley static void
2882908d778SJames Bottomley asd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha)
2892908d778SJames Bottomley {
2902908d778SJames Bottomley 	int i;
2912908d778SJames Bottomley 
2922908d778SJames Bottomley 	/* Zero OCM */
2932908d778SJames Bottomley 	for (i = 0; i < OCM_MAX_SIZE; i += 4)
2942908d778SJames Bottomley 		asd_write_ocm_dword(asd_ha, i, 0);
2952908d778SJames Bottomley 
2962908d778SJames Bottomley 	/* Write Dir */
2972908d778SJames Bottomley 	asd_write_ocm_seg(asd_ha, &OCMDirInit, 0,
2982908d778SJames Bottomley 			  sizeof(struct asd_ocm_dir));
2992908d778SJames Bottomley 
3002908d778SJames Bottomley 	/* Write Dir Entries */
3012908d778SJames Bottomley 	for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++)
3022908d778SJames Bottomley 		asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i],
3032908d778SJames Bottomley 				  sizeof(struct asd_ocm_dir) +
3042908d778SJames Bottomley 				  (i * sizeof(struct asd_ocm_dir_ent))
3052908d778SJames Bottomley 				  , sizeof(struct asd_ocm_dir_ent));
3062908d778SJames Bottomley 
3072908d778SJames Bottomley }
3082908d778SJames Bottomley 
3092908d778SJames Bottomley static int
3102908d778SJames Bottomley asd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha)
3112908d778SJames Bottomley {
3122908d778SJames Bottomley 	struct pci_dev *pcidev = asd_ha->pcidev;
3132908d778SJames Bottomley 	u32 reg;
3142908d778SJames Bottomley 	int err = 0;
3152908d778SJames Bottomley 	u32 v;
3162908d778SJames Bottomley 
3172908d778SJames Bottomley 	/* check if OCM has been initialized by BIOS */
3182908d778SJames Bottomley 	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
3192908d778SJames Bottomley 
3202908d778SJames Bottomley 	if (!(reg & OCMINITIALIZED)) {
3212908d778SJames Bottomley 		err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v);
3222908d778SJames Bottomley 		if (err) {
3232908d778SJames Bottomley 			asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n",
3242908d778SJames Bottomley 					pci_name(pcidev));
3252908d778SJames Bottomley 			goto out;
3262908d778SJames Bottomley 		}
3272908d778SJames Bottomley 
3282908d778SJames Bottomley 		printk(KERN_INFO "OCM is not initialized by BIOS,"
3292908d778SJames Bottomley 		       "reinitialize it and ignore it, current IntrptStatus"
3302908d778SJames Bottomley 		       "is 0x%x\n", v);
3312908d778SJames Bottomley 
3322908d778SJames Bottomley 		if (v)
3332908d778SJames Bottomley 			err = pci_write_config_dword(pcidev,
3342908d778SJames Bottomley 						     PCIC_INTRPT_STAT, v);
3352908d778SJames Bottomley 		if (err) {
3362908d778SJames Bottomley 			asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n",
3372908d778SJames Bottomley 					pci_name(pcidev));
3382908d778SJames Bottomley 			goto out;
3392908d778SJames Bottomley 		}
3402908d778SJames Bottomley 
3412908d778SJames Bottomley 		asd_hwi_initialize_ocm_dir(asd_ha);
3422908d778SJames Bottomley 
3432908d778SJames Bottomley 	}
3442908d778SJames Bottomley out:
3452908d778SJames Bottomley 	return err;
3462908d778SJames Bottomley }
3472908d778SJames Bottomley 
3482908d778SJames Bottomley /**
3492908d778SJames Bottomley  * asd_read_ocm - read on chip memory (OCM)
3502908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
3512908d778SJames Bottomley  */
3522908d778SJames Bottomley int asd_read_ocm(struct asd_ha_struct *asd_ha)
3532908d778SJames Bottomley {
3542908d778SJames Bottomley 	int err;
3552908d778SJames Bottomley 	struct asd_ocm_dir *dir;
3562908d778SJames Bottomley 
3572908d778SJames Bottomley 	if (asd_hwi_check_ocm_access(asd_ha))
3582908d778SJames Bottomley 		return -1;
3592908d778SJames Bottomley 
3602908d778SJames Bottomley 	dir = kmalloc(sizeof(*dir), GFP_KERNEL);
3612908d778SJames Bottomley 	if (!dir) {
3622908d778SJames Bottomley 		asd_printk("no memory for ocm dir\n");
3632908d778SJames Bottomley 		return -ENOMEM;
3642908d778SJames Bottomley 	}
3652908d778SJames Bottomley 
3662908d778SJames Bottomley 	err = asd_read_ocm_dir(asd_ha, dir, 0);
3672908d778SJames Bottomley 	if (err)
3682908d778SJames Bottomley 		goto out;
3692908d778SJames Bottomley 
3702908d778SJames Bottomley 	err = asd_get_bios_chim(asd_ha, dir);
3712908d778SJames Bottomley out:
3722908d778SJames Bottomley 	kfree(dir);
3732908d778SJames Bottomley 	return err;
3742908d778SJames Bottomley }
3752908d778SJames Bottomley 
3762908d778SJames Bottomley /* ---------- FLASH stuff ---------- */
3772908d778SJames Bottomley 
3782908d778SJames Bottomley #define FLASH_RESET			0xF0
3792908d778SJames Bottomley 
380d297a5d5SAndrew Morton #define ASD_FLASH_SIZE                  0x200000
3812908d778SJames Bottomley #define FLASH_DIR_COOKIE                "*** ADAPTEC FLASH DIRECTORY *** "
3822908d778SJames Bottomley #define FLASH_NEXT_ENTRY_OFFS		0x2000
3832908d778SJames Bottomley #define FLASH_MAX_DIR_ENTRIES		32
3842908d778SJames Bottomley 
3852908d778SJames Bottomley #define FLASH_DE_TYPE_MASK              0x3FFFFFFF
3862908d778SJames Bottomley #define FLASH_DE_MS                     0x120
3872908d778SJames Bottomley #define FLASH_DE_CTRL_A_USER            0xE0
3882908d778SJames Bottomley 
3892908d778SJames Bottomley struct asd_flash_de {
3902908d778SJames Bottomley 	__le32   type;
3912908d778SJames Bottomley 	__le32   offs;
3922908d778SJames Bottomley 	__le32   pad_size;
3932908d778SJames Bottomley 	__le32   image_size;
3942908d778SJames Bottomley 	__le32   chksum;
3952908d778SJames Bottomley 	u8       _r[12];
3962908d778SJames Bottomley 	u8       version[32];
3972908d778SJames Bottomley } __attribute__ ((packed));
3982908d778SJames Bottomley 
3992908d778SJames Bottomley struct asd_flash_dir {
4002908d778SJames Bottomley 	u8    cookie[32];
4012908d778SJames Bottomley 	__le32   rev;		  /* 2 */
4022908d778SJames Bottomley 	__le32   chksum;
4032908d778SJames Bottomley 	__le32   chksum_antidote;
4042908d778SJames Bottomley 	__le32   bld;
4052908d778SJames Bottomley 	u8    bld_id[32];	  /* build id data */
4062908d778SJames Bottomley 	u8    ver_data[32];	  /* date and time of build */
4072908d778SJames Bottomley 	__le32   ae_mask;
4082908d778SJames Bottomley 	__le32   v_mask;
4092908d778SJames Bottomley 	__le32   oc_mask;
4102908d778SJames Bottomley 	u8    _r[20];
4112908d778SJames Bottomley 	struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES];
4122908d778SJames Bottomley } __attribute__ ((packed));
4132908d778SJames Bottomley 
4142908d778SJames Bottomley struct asd_manuf_sec {
4152908d778SJames Bottomley 	char  sig[2];		  /* 'S', 'M' */
4162908d778SJames Bottomley 	u16   offs_next;
4172908d778SJames Bottomley 	u8    maj;           /* 0 */
4182908d778SJames Bottomley 	u8    min;           /* 0 */
4192908d778SJames Bottomley 	u16   chksum;
4202908d778SJames Bottomley 	u16   size;
4212908d778SJames Bottomley 	u8    _r[6];
4222908d778SJames Bottomley 	u8    sas_addr[SAS_ADDR_SIZE];
4232908d778SJames Bottomley 	u8    pcba_sn[ASD_PCBA_SN_SIZE];
4242908d778SJames Bottomley 	/* Here start the other segments */
4252908d778SJames Bottomley 	u8    linked_list[0];
4262908d778SJames Bottomley } __attribute__ ((packed));
4272908d778SJames Bottomley 
4282908d778SJames Bottomley struct asd_manuf_phy_desc {
4292908d778SJames Bottomley 	u8    state;         /* low 4 bits */
43086b9c4c1SAlexis Bruemmer #define MS_PHY_STATE_ENABLED    0
4312908d778SJames Bottomley #define MS_PHY_STATE_REPORTED   1
4322908d778SJames Bottomley #define MS_PHY_STATE_HIDDEN     2
4332908d778SJames Bottomley 	u8    phy_id;
4342908d778SJames Bottomley 	u16   _r;
4352908d778SJames Bottomley 	u8    phy_control_0; /* mode 5 reg 0x160 */
4362908d778SJames Bottomley 	u8    phy_control_1; /* mode 5 reg 0x161 */
4372908d778SJames Bottomley 	u8    phy_control_2; /* mode 5 reg 0x162 */
4382908d778SJames Bottomley 	u8    phy_control_3; /* mode 5 reg 0x163 */
4392908d778SJames Bottomley } __attribute__ ((packed));
4402908d778SJames Bottomley 
4412908d778SJames Bottomley struct asd_manuf_phy_param {
4422908d778SJames Bottomley 	char  sig[2];		  /* 'P', 'M' */
4432908d778SJames Bottomley 	u16   next;
4442908d778SJames Bottomley 	u8    maj;           /* 0 */
4452908d778SJames Bottomley 	u8    min;           /* 2 */
4462908d778SJames Bottomley 	u8    num_phy_desc;  /* 8 */
4472908d778SJames Bottomley 	u8    phy_desc_size; /* 8 */
4482908d778SJames Bottomley 	u8    _r[3];
4492908d778SJames Bottomley 	u8    usage_model_id;
4502908d778SJames Bottomley 	u32   _r2;
4512908d778SJames Bottomley 	struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS];
4522908d778SJames Bottomley } __attribute__ ((packed));
4532908d778SJames Bottomley 
4542908d778SJames Bottomley #if 0
4552908d778SJames Bottomley static const char *asd_sb_type[] = {
4562908d778SJames Bottomley 	"unknown",
4572908d778SJames Bottomley 	"SGPIO",
4582908d778SJames Bottomley 	[2 ... 0x7F] = "unknown",
4592908d778SJames Bottomley 	[0x80] = "ADPT_I2C",
4602908d778SJames Bottomley 	[0x81 ... 0xFF] = "VENDOR_UNIQUExx"
4612908d778SJames Bottomley };
4622908d778SJames Bottomley #endif
4632908d778SJames Bottomley 
4642908d778SJames Bottomley struct asd_ms_sb_desc {
4652908d778SJames Bottomley 	u8    type;
4662908d778SJames Bottomley 	u8    node_desc_index;
4672908d778SJames Bottomley 	u8    conn_desc_index;
4682908d778SJames Bottomley 	u8    _recvd[0];
4692908d778SJames Bottomley } __attribute__ ((packed));
4702908d778SJames Bottomley 
4712908d778SJames Bottomley #if 0
4722908d778SJames Bottomley static const char *asd_conn_type[] = {
4732908d778SJames Bottomley 	[0 ... 7] = "unknown",
4742908d778SJames Bottomley 	"SFF8470",
4752908d778SJames Bottomley 	"SFF8482",
4762908d778SJames Bottomley 	"SFF8484",
4772908d778SJames Bottomley 	[0x80] = "PCIX_DAUGHTER0",
4782908d778SJames Bottomley 	[0x81] = "SAS_DAUGHTER0",
4792908d778SJames Bottomley 	[0x82 ... 0xFF] = "VENDOR_UNIQUExx"
4802908d778SJames Bottomley };
4812908d778SJames Bottomley 
4822908d778SJames Bottomley static const char *asd_conn_location[] = {
4832908d778SJames Bottomley 	"unknown",
4842908d778SJames Bottomley 	"internal",
4852908d778SJames Bottomley 	"external",
4862908d778SJames Bottomley 	"board_to_board",
4872908d778SJames Bottomley };
4882908d778SJames Bottomley #endif
4892908d778SJames Bottomley 
4902908d778SJames Bottomley struct asd_ms_conn_desc {
4912908d778SJames Bottomley 	u8    type;
4922908d778SJames Bottomley 	u8    location;
4932908d778SJames Bottomley 	u8    num_sideband_desc;
4942908d778SJames Bottomley 	u8    size_sideband_desc;
4952908d778SJames Bottomley 	u32   _resvd;
4962908d778SJames Bottomley 	u8    name[16];
4972908d778SJames Bottomley 	struct asd_ms_sb_desc sb_desc[0];
4982908d778SJames Bottomley } __attribute__ ((packed));
4992908d778SJames Bottomley 
5002908d778SJames Bottomley struct asd_nd_phy_desc {
5012908d778SJames Bottomley 	u8    vp_attch_type;
5022908d778SJames Bottomley 	u8    attch_specific[0];
5032908d778SJames Bottomley } __attribute__ ((packed));
5042908d778SJames Bottomley 
5052908d778SJames Bottomley #if 0
5062908d778SJames Bottomley static const char *asd_node_type[] = {
5072908d778SJames Bottomley 	"IOP",
5082908d778SJames Bottomley 	"IO_CONTROLLER",
5092908d778SJames Bottomley 	"EXPANDER",
5102908d778SJames Bottomley 	"PORT_MULTIPLIER",
5112908d778SJames Bottomley 	"PORT_MULTIPLEXER",
5122908d778SJames Bottomley 	"MULTI_DROP_I2C_BUS",
5132908d778SJames Bottomley };
5142908d778SJames Bottomley #endif
5152908d778SJames Bottomley 
5162908d778SJames Bottomley struct asd_ms_node_desc {
5172908d778SJames Bottomley 	u8    type;
5182908d778SJames Bottomley 	u8    num_phy_desc;
5192908d778SJames Bottomley 	u8    size_phy_desc;
5202908d778SJames Bottomley 	u8    _resvd;
5212908d778SJames Bottomley 	u8    name[16];
5222908d778SJames Bottomley 	struct asd_nd_phy_desc phy_desc[0];
5232908d778SJames Bottomley } __attribute__ ((packed));
5242908d778SJames Bottomley 
5252908d778SJames Bottomley struct asd_ms_conn_map {
5262908d778SJames Bottomley 	char  sig[2];		  /* 'M', 'C' */
5272908d778SJames Bottomley 	__le16 next;
5282908d778SJames Bottomley 	u8    maj;		  /* 0 */
5292908d778SJames Bottomley 	u8    min;		  /* 0 */
5302908d778SJames Bottomley 	__le16 cm_size;		  /* size of this struct */
5312908d778SJames Bottomley 	u8    num_conn;
5322908d778SJames Bottomley 	u8    conn_size;
5332908d778SJames Bottomley 	u8    num_nodes;
5342908d778SJames Bottomley 	u8    usage_model_id;
5352908d778SJames Bottomley 	u32   _resvd;
5362908d778SJames Bottomley 	struct asd_ms_conn_desc conn_desc[0];
5372908d778SJames Bottomley 	struct asd_ms_node_desc node_desc[0];
5382908d778SJames Bottomley } __attribute__ ((packed));
5392908d778SJames Bottomley 
5402908d778SJames Bottomley struct asd_ctrla_phy_entry {
5412908d778SJames Bottomley 	u8    sas_addr[SAS_ADDR_SIZE];
5422908d778SJames Bottomley 	u8    sas_link_rates;  /* max in hi bits, min in low bits */
5432908d778SJames Bottomley 	u8    flags;
5442908d778SJames Bottomley 	u8    sata_link_rates;
5452908d778SJames Bottomley 	u8    _r[5];
5462908d778SJames Bottomley } __attribute__ ((packed));
5472908d778SJames Bottomley 
5482908d778SJames Bottomley struct asd_ctrla_phy_settings {
5492908d778SJames Bottomley 	u8    id0;		  /* P'h'y */
5502908d778SJames Bottomley 	u8    _r;
5512908d778SJames Bottomley 	u16   next;
5522908d778SJames Bottomley 	u8    num_phys;	      /* number of PHYs in the PCI function */
5532908d778SJames Bottomley 	u8    _r2[3];
5542908d778SJames Bottomley 	struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS];
5552908d778SJames Bottomley } __attribute__ ((packed));
5562908d778SJames Bottomley 
5572908d778SJames Bottomley struct asd_ll_el {
5582908d778SJames Bottomley 	u8   id0;
5592908d778SJames Bottomley 	u8   id1;
5602908d778SJames Bottomley 	__le16  next;
5612908d778SJames Bottomley 	u8   something_here[0];
5622908d778SJames Bottomley } __attribute__ ((packed));
5632908d778SJames Bottomley 
5642908d778SJames Bottomley static int asd_poll_flash(struct asd_ha_struct *asd_ha)
5652908d778SJames Bottomley {
5662908d778SJames Bottomley 	int c;
5672908d778SJames Bottomley 	u8 d;
5682908d778SJames Bottomley 
5692908d778SJames Bottomley 	for (c = 5000; c > 0; c--) {
5702908d778SJames Bottomley 		d  = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
5712908d778SJames Bottomley 		d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar);
5722908d778SJames Bottomley 		if (!d)
5732908d778SJames Bottomley 			return 0;
5742908d778SJames Bottomley 		udelay(5);
5752908d778SJames Bottomley 	}
5762908d778SJames Bottomley 	return -ENOENT;
5772908d778SJames Bottomley }
5782908d778SJames Bottomley 
5792908d778SJames Bottomley static int asd_reset_flash(struct asd_ha_struct *asd_ha)
5802908d778SJames Bottomley {
5812908d778SJames Bottomley 	int err;
5822908d778SJames Bottomley 
5832908d778SJames Bottomley 	err = asd_poll_flash(asd_ha);
5842908d778SJames Bottomley 	if (err)
5852908d778SJames Bottomley 		return err;
5862908d778SJames Bottomley 	asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET);
5872908d778SJames Bottomley 	err = asd_poll_flash(asd_ha);
5882908d778SJames Bottomley 
5892908d778SJames Bottomley 	return err;
5902908d778SJames Bottomley }
5912908d778SJames Bottomley 
5922908d778SJames Bottomley static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
5932908d778SJames Bottomley 				     void *buffer, u32 offs, int size)
5942908d778SJames Bottomley {
5952908d778SJames Bottomley 	asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
5962908d778SJames Bottomley 			    size);
5972908d778SJames Bottomley 	return 0;
5982908d778SJames Bottomley }
5992908d778SJames Bottomley 
6002908d778SJames Bottomley /**
6012908d778SJames Bottomley  * asd_find_flash_dir - finds and reads the flash directory
6022908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
6032908d778SJames Bottomley  * @flash_dir: pointer to flash directory structure
6042908d778SJames Bottomley  *
6052908d778SJames Bottomley  * If found, the flash directory segment will be copied to
6062908d778SJames Bottomley  * @flash_dir.  Return 1 if found, 0 if not.
6072908d778SJames Bottomley  */
6082908d778SJames Bottomley static int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
6092908d778SJames Bottomley 			      struct asd_flash_dir *flash_dir)
6102908d778SJames Bottomley {
6112908d778SJames Bottomley 	u32 v;
612d297a5d5SAndrew Morton 	for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
6132908d778SJames Bottomley 		asd_read_flash_seg(asd_ha, flash_dir, v,
6142908d778SJames Bottomley 				   sizeof(FLASH_DIR_COOKIE)-1);
6152908d778SJames Bottomley 		if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
6162908d778SJames Bottomley 			   sizeof(FLASH_DIR_COOKIE)-1) == 0) {
6172908d778SJames Bottomley 			asd_ha->hw_prof.flash.dir_offs = v;
6182908d778SJames Bottomley 			asd_read_flash_seg(asd_ha, flash_dir, v,
6192908d778SJames Bottomley 					   sizeof(*flash_dir));
6202908d778SJames Bottomley 			return 1;
6212908d778SJames Bottomley 		}
6222908d778SJames Bottomley 	}
6232908d778SJames Bottomley 	return 0;
6242908d778SJames Bottomley }
6252908d778SJames Bottomley 
6262908d778SJames Bottomley static int asd_flash_getid(struct asd_ha_struct *asd_ha)
6272908d778SJames Bottomley {
6282908d778SJames Bottomley 	int err = 0;
629f2d719c6SAlexis Bruemmer 	u32 reg;
6302908d778SJames Bottomley 
6312908d778SJames Bottomley 	reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
6322908d778SJames Bottomley 
6332908d778SJames Bottomley 	if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
6342908d778SJames Bottomley 				  &asd_ha->hw_prof.flash.bar)) {
6352908d778SJames Bottomley 		asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
6362908d778SJames Bottomley 			   pci_name(asd_ha->pcidev));
6372908d778SJames Bottomley 		return -ENOENT;
6382908d778SJames Bottomley 	}
6392908d778SJames Bottomley 	asd_ha->hw_prof.flash.present = 1;
6402908d778SJames Bottomley 	asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0;
6412908d778SJames Bottomley 	err = asd_reset_flash(asd_ha);
6422908d778SJames Bottomley 	if (err) {
6432908d778SJames Bottomley 		ASD_DPRINTK("couldn't reset flash(%d)\n", err);
6442908d778SJames Bottomley 		return err;
6452908d778SJames Bottomley 	}
6462908d778SJames Bottomley 	return 0;
6472908d778SJames Bottomley }
6482908d778SJames Bottomley 
6492908d778SJames Bottomley static u16 asd_calc_flash_chksum(u16 *p, int size)
6502908d778SJames Bottomley {
6512908d778SJames Bottomley 	u16 chksum = 0;
6522908d778SJames Bottomley 
6532908d778SJames Bottomley 	while (size-- > 0)
6542908d778SJames Bottomley 		chksum += *p++;
6552908d778SJames Bottomley 
6562908d778SJames Bottomley 	return chksum;
6572908d778SJames Bottomley }
6582908d778SJames Bottomley 
6592908d778SJames Bottomley 
6602908d778SJames Bottomley static int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type,
6612908d778SJames Bottomley 			     u32 *offs, u32 *size)
6622908d778SJames Bottomley {
6632908d778SJames Bottomley 	int i;
6642908d778SJames Bottomley 	struct asd_flash_de *de;
6652908d778SJames Bottomley 
6662908d778SJames Bottomley 	for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) {
6672908d778SJames Bottomley 		u32 type = le32_to_cpu(flash_dir->dir_entry[i].type);
6682908d778SJames Bottomley 
6692908d778SJames Bottomley 		type &= FLASH_DE_TYPE_MASK;
6702908d778SJames Bottomley 		if (type == entry_type)
6712908d778SJames Bottomley 			break;
6722908d778SJames Bottomley 	}
6732908d778SJames Bottomley 	if (i >= FLASH_MAX_DIR_ENTRIES)
6742908d778SJames Bottomley 		return -ENOENT;
6752908d778SJames Bottomley 	de = &flash_dir->dir_entry[i];
6762908d778SJames Bottomley 	*offs = le32_to_cpu(de->offs);
6772908d778SJames Bottomley 	*size = le32_to_cpu(de->pad_size);
6782908d778SJames Bottomley 	return 0;
6792908d778SJames Bottomley }
6802908d778SJames Bottomley 
6812908d778SJames Bottomley static int asd_validate_ms(struct asd_manuf_sec *ms)
6822908d778SJames Bottomley {
6832908d778SJames Bottomley 	if (ms->sig[0] != 'S' || ms->sig[1] != 'M') {
6842908d778SJames Bottomley 		ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n",
6852908d778SJames Bottomley 			    ms->sig[0], ms->sig[1]);
6862908d778SJames Bottomley 		return -ENOENT;
6872908d778SJames Bottomley 	}
6882908d778SJames Bottomley 	if (ms->maj != 0) {
6892908d778SJames Bottomley 		asd_printk("unsupported manuf. sector. major version:%x\n",
6902908d778SJames Bottomley 			   ms->maj);
6912908d778SJames Bottomley 		return -ENOENT;
6922908d778SJames Bottomley 	}
6932908d778SJames Bottomley 	ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next);
6942908d778SJames Bottomley 	ms->chksum = le16_to_cpu((__force __le16) ms->chksum);
6952908d778SJames Bottomley 	ms->size = le16_to_cpu((__force __le16) ms->size);
6962908d778SJames Bottomley 
6972908d778SJames Bottomley 	if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) {
6982908d778SJames Bottomley 		asd_printk("failed manuf sector checksum\n");
6992908d778SJames Bottomley 	}
7002908d778SJames Bottomley 
7012908d778SJames Bottomley 	return 0;
7022908d778SJames Bottomley }
7032908d778SJames Bottomley 
7042908d778SJames Bottomley static int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha,
7052908d778SJames Bottomley 			       struct asd_manuf_sec *ms)
7062908d778SJames Bottomley {
7072908d778SJames Bottomley 	memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE);
7082908d778SJames Bottomley 	return 0;
7092908d778SJames Bottomley }
7102908d778SJames Bottomley 
7112908d778SJames Bottomley static int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha,
7122908d778SJames Bottomley 			      struct asd_manuf_sec *ms)
7132908d778SJames Bottomley {
7142908d778SJames Bottomley 	memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE);
7152908d778SJames Bottomley 	asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0';
7162908d778SJames Bottomley 	return 0;
7172908d778SJames Bottomley }
7182908d778SJames Bottomley 
7192908d778SJames Bottomley /**
7202908d778SJames Bottomley  * asd_find_ll_by_id - find a linked list entry by its id
7212908d778SJames Bottomley  * @start: void pointer to the first element in the linked list
7222908d778SJames Bottomley  * @id0: the first byte of the id  (offs 0)
7232908d778SJames Bottomley  * @id1: the second byte of the id (offs 1)
7242908d778SJames Bottomley  *
7252908d778SJames Bottomley  * @start has to be the _base_ element start, since the
7262908d778SJames Bottomley  * linked list entries's offset is from this pointer.
7272908d778SJames Bottomley  * Some linked list entries use only the first id, in which case
7282908d778SJames Bottomley  * you can pass 0xFF for the second.
7292908d778SJames Bottomley  */
7302908d778SJames Bottomley static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
7312908d778SJames Bottomley {
7322908d778SJames Bottomley 	struct asd_ll_el *el = start;
7332908d778SJames Bottomley 
7342908d778SJames Bottomley 	do {
7352908d778SJames Bottomley 		switch (id1) {
7362908d778SJames Bottomley 		default:
7372908d778SJames Bottomley 			if (el->id1 == id1)
7382908d778SJames Bottomley 		case 0xFF:
7392908d778SJames Bottomley 				if (el->id0 == id0)
7402908d778SJames Bottomley 					return el;
7412908d778SJames Bottomley 		}
7422908d778SJames Bottomley 		el = start + le16_to_cpu(el->next);
7432908d778SJames Bottomley 	} while (el != start);
7442908d778SJames Bottomley 
7452908d778SJames Bottomley 	return NULL;
7462908d778SJames Bottomley }
7472908d778SJames Bottomley 
7482908d778SJames Bottomley /**
7492908d778SJames Bottomley  * asd_ms_get_phy_params - get phy parameters from the manufacturing sector
7502908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
7512908d778SJames Bottomley  * @manuf_sec: pointer to the manufacturing sector
7522908d778SJames Bottomley  *
7532908d778SJames Bottomley  * The manufacturing sector contans also the linked list of sub-segments,
7542908d778SJames Bottomley  * since when it was read, its size was taken from the flash directory,
7552908d778SJames Bottomley  * not from the structure size.
7562908d778SJames Bottomley  *
7572908d778SJames Bottomley  * HIDDEN phys do not count in the total count.  REPORTED phys cannot
7582908d778SJames Bottomley  * be enabled but are reported and counted towards the total.
75986b9c4c1SAlexis Bruemmer  * ENABLED phys are enabled by default and count towards the total.
7602908d778SJames Bottomley  * The absolute total phy number is ASD_MAX_PHYS.  hw_prof->num_phys
7612908d778SJames Bottomley  * merely specifies the number of phys the host adapter decided to
7622908d778SJames Bottomley  * report.  E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
76386b9c4c1SAlexis Bruemmer  * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED.
7642908d778SJames Bottomley  * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
7652908d778SJames Bottomley  * are actually enabled (enabled by default, max number of phys
7662908d778SJames Bottomley  * enableable in this case).
7672908d778SJames Bottomley  */
7682908d778SJames Bottomley static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
7692908d778SJames Bottomley 				 struct asd_manuf_sec *manuf_sec)
7702908d778SJames Bottomley {
7712908d778SJames Bottomley 	int i;
7722908d778SJames Bottomley 	int en_phys = 0;
7732908d778SJames Bottomley 	int rep_phys = 0;
7742908d778SJames Bottomley 	struct asd_manuf_phy_param *phy_param;
7752908d778SJames Bottomley 	struct asd_manuf_phy_param dflt_phy_param;
7762908d778SJames Bottomley 
7772908d778SJames Bottomley 	phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M');
7782908d778SJames Bottomley 	if (!phy_param) {
7792908d778SJames Bottomley 		ASD_DPRINTK("ms: no phy parameters found\n");
7802908d778SJames Bottomley 		ASD_DPRINTK("ms: Creating default phy parameters\n");
7812908d778SJames Bottomley 		dflt_phy_param.sig[0] = 'P';
7822908d778SJames Bottomley 		dflt_phy_param.sig[1] = 'M';
7832908d778SJames Bottomley 		dflt_phy_param.maj = 0;
7842908d778SJames Bottomley 		dflt_phy_param.min = 2;
7852908d778SJames Bottomley 		dflt_phy_param.num_phy_desc = 8;
7862908d778SJames Bottomley 		dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc);
7872908d778SJames Bottomley 		for (i =0; i < ASD_MAX_PHYS; i++) {
7882908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].state = 0;
7892908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_id = i;
7902908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6;
7912908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_1 = 0x10;
7922908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_2 = 0x43;
7932908d778SJames Bottomley 			dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb;
7942908d778SJames Bottomley 		}
7952908d778SJames Bottomley 
7962908d778SJames Bottomley 		phy_param = &dflt_phy_param;
7972908d778SJames Bottomley 
7982908d778SJames Bottomley 	}
7992908d778SJames Bottomley 
8002908d778SJames Bottomley 	if (phy_param->maj != 0) {
8012908d778SJames Bottomley 		asd_printk("unsupported manuf. phy param major version:0x%x\n",
8022908d778SJames Bottomley 			   phy_param->maj);
8032908d778SJames Bottomley 		return -ENOENT;
8042908d778SJames Bottomley 	}
8052908d778SJames Bottomley 
8062908d778SJames Bottomley 	ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc);
8072908d778SJames Bottomley 	asd_ha->hw_prof.enabled_phys = 0;
8082908d778SJames Bottomley 	for (i = 0; i < phy_param->num_phy_desc; i++) {
8092908d778SJames Bottomley 		struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i];
8102908d778SJames Bottomley 		switch (pd->state & 0xF) {
8112908d778SJames Bottomley 		case MS_PHY_STATE_HIDDEN:
8122908d778SJames Bottomley 			ASD_DPRINTK("ms: phy%d: HIDDEN\n", i);
8132908d778SJames Bottomley 			continue;
8142908d778SJames Bottomley 		case MS_PHY_STATE_REPORTED:
8152908d778SJames Bottomley 			ASD_DPRINTK("ms: phy%d: REPORTED\n", i);
8162908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
8172908d778SJames Bottomley 			rep_phys++;
8182908d778SJames Bottomley 			continue;
81986b9c4c1SAlexis Bruemmer 		case MS_PHY_STATE_ENABLED:
82086b9c4c1SAlexis Bruemmer 			ASD_DPRINTK("ms: phy%d: ENABLED\n", i);
8212908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys |= (1 << i);
8222908d778SJames Bottomley 			en_phys++;
8232908d778SJames Bottomley 			break;
8242908d778SJames Bottomley 		}
8252908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0;
8262908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1;
8272908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2;
8282908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3;
8292908d778SJames Bottomley 	}
8302908d778SJames Bottomley 	asd_ha->hw_prof.max_phys = rep_phys + en_phys;
8312908d778SJames Bottomley 	asd_ha->hw_prof.num_phys = en_phys;
8322908d778SJames Bottomley 	ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n",
8332908d778SJames Bottomley 		    asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys);
8342908d778SJames Bottomley 	ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys);
8352908d778SJames Bottomley 	return 0;
8362908d778SJames Bottomley }
8372908d778SJames Bottomley 
8382908d778SJames Bottomley static int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha,
8392908d778SJames Bottomley 				    struct asd_manuf_sec *manuf_sec)
8402908d778SJames Bottomley {
8412908d778SJames Bottomley 	struct asd_ms_conn_map *cm;
8422908d778SJames Bottomley 
8432908d778SJames Bottomley 	cm = asd_find_ll_by_id(manuf_sec, 'M', 'C');
8442908d778SJames Bottomley 	if (!cm) {
8452908d778SJames Bottomley 		ASD_DPRINTK("ms: no connector map found\n");
8462908d778SJames Bottomley 		return 0;
8472908d778SJames Bottomley 	}
8482908d778SJames Bottomley 
8492908d778SJames Bottomley 	if (cm->maj != 0) {
8502908d778SJames Bottomley 		ASD_DPRINTK("ms: unsupported: connector map major version 0x%x"
8512908d778SJames Bottomley 			    "\n", cm->maj);
8522908d778SJames Bottomley 		return -ENOENT;
8532908d778SJames Bottomley 	}
8542908d778SJames Bottomley 
8552908d778SJames Bottomley 	/* XXX */
8562908d778SJames Bottomley 
8572908d778SJames Bottomley 	return 0;
8582908d778SJames Bottomley }
8592908d778SJames Bottomley 
8602908d778SJames Bottomley 
8612908d778SJames Bottomley /**
8622908d778SJames Bottomley  * asd_process_ms - find and extract information from the manufacturing sector
8632908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
8642908d778SJames Bottomley  * @flash_dir: pointer to the flash directory
8652908d778SJames Bottomley  */
8662908d778SJames Bottomley static int asd_process_ms(struct asd_ha_struct *asd_ha,
8672908d778SJames Bottomley 			  struct asd_flash_dir *flash_dir)
8682908d778SJames Bottomley {
8692908d778SJames Bottomley 	int err;
8702908d778SJames Bottomley 	struct asd_manuf_sec *manuf_sec;
8712908d778SJames Bottomley 	u32 offs, size;
8722908d778SJames Bottomley 
8732908d778SJames Bottomley 	err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size);
8742908d778SJames Bottomley 	if (err) {
8752908d778SJames Bottomley 		ASD_DPRINTK("Couldn't find the manuf. sector\n");
8762908d778SJames Bottomley 		goto out;
8772908d778SJames Bottomley 	}
8782908d778SJames Bottomley 
8792908d778SJames Bottomley 	if (size == 0)
8802908d778SJames Bottomley 		goto out;
8812908d778SJames Bottomley 
8822908d778SJames Bottomley 	err = -ENOMEM;
8832908d778SJames Bottomley 	manuf_sec = kmalloc(size, GFP_KERNEL);
8842908d778SJames Bottomley 	if (!manuf_sec) {
8852908d778SJames Bottomley 		ASD_DPRINTK("no mem for manuf sector\n");
8862908d778SJames Bottomley 		goto out;
8872908d778SJames Bottomley 	}
8882908d778SJames Bottomley 
8892908d778SJames Bottomley 	err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size);
8902908d778SJames Bottomley 	if (err) {
8912908d778SJames Bottomley 		ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n",
8922908d778SJames Bottomley 			    offs, size);
8932908d778SJames Bottomley 		goto out2;
8942908d778SJames Bottomley 	}
8952908d778SJames Bottomley 
8962908d778SJames Bottomley 	err = asd_validate_ms(manuf_sec);
8972908d778SJames Bottomley 	if (err) {
8982908d778SJames Bottomley 		ASD_DPRINTK("couldn't validate manuf sector\n");
8992908d778SJames Bottomley 		goto out2;
9002908d778SJames Bottomley 	}
9012908d778SJames Bottomley 
9022908d778SJames Bottomley 	err = asd_ms_get_sas_addr(asd_ha, manuf_sec);
9032908d778SJames Bottomley 	if (err) {
9042908d778SJames Bottomley 		ASD_DPRINTK("couldn't read the SAS_ADDR\n");
9052908d778SJames Bottomley 		goto out2;
9062908d778SJames Bottomley 	}
9072908d778SJames Bottomley 	ASD_DPRINTK("manuf sect SAS_ADDR %llx\n",
9082908d778SJames Bottomley 		    SAS_ADDR(asd_ha->hw_prof.sas_addr));
9092908d778SJames Bottomley 
9102908d778SJames Bottomley 	err = asd_ms_get_pcba_sn(asd_ha, manuf_sec);
9112908d778SJames Bottomley 	if (err) {
9122908d778SJames Bottomley 		ASD_DPRINTK("couldn't read the PCBA SN\n");
9132908d778SJames Bottomley 		goto out2;
9142908d778SJames Bottomley 	}
9152908d778SJames Bottomley 	ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn);
9162908d778SJames Bottomley 
9172908d778SJames Bottomley 	err = asd_ms_get_phy_params(asd_ha, manuf_sec);
9182908d778SJames Bottomley 	if (err) {
9192908d778SJames Bottomley 		ASD_DPRINTK("ms: couldn't get phy parameters\n");
9202908d778SJames Bottomley 		goto out2;
9212908d778SJames Bottomley 	}
9222908d778SJames Bottomley 
9232908d778SJames Bottomley 	err = asd_ms_get_connector_map(asd_ha, manuf_sec);
9242908d778SJames Bottomley 	if (err) {
9252908d778SJames Bottomley 		ASD_DPRINTK("ms: couldn't get connector map\n");
9262908d778SJames Bottomley 		goto out2;
9272908d778SJames Bottomley 	}
9282908d778SJames Bottomley 
9292908d778SJames Bottomley out2:
9302908d778SJames Bottomley 	kfree(manuf_sec);
9312908d778SJames Bottomley out:
9322908d778SJames Bottomley 	return err;
9332908d778SJames Bottomley }
9342908d778SJames Bottomley 
9352908d778SJames Bottomley static int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha,
9362908d778SJames Bottomley 					  struct asd_ctrla_phy_settings *ps)
9372908d778SJames Bottomley {
9382908d778SJames Bottomley 	int i;
9392908d778SJames Bottomley 	for (i = 0; i < ps->num_phys; i++) {
9402908d778SJames Bottomley 		struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i];
9412908d778SJames Bottomley 
9422908d778SJames Bottomley 		if (!PHY_ENABLED(asd_ha, i))
9432908d778SJames Bottomley 			continue;
9442908d778SJames Bottomley 		if (*(u64 *)pe->sas_addr == 0) {
9452908d778SJames Bottomley 			asd_ha->hw_prof.enabled_phys &= ~(1 << i);
9462908d778SJames Bottomley 			continue;
9472908d778SJames Bottomley 		}
9482908d778SJames Bottomley 		/* This is the SAS address which should be sent in IDENTIFY. */
9492908d778SJames Bottomley 		memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr,
9502908d778SJames Bottomley 		       SAS_ADDR_SIZE);
9512908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].max_sas_lrate =
9522908d778SJames Bottomley 			(pe->sas_link_rates & 0xF0) >> 4;
9532908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].min_sas_lrate =
9542908d778SJames Bottomley 			(pe->sas_link_rates & 0x0F);
9552908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].max_sata_lrate =
9562908d778SJames Bottomley 			(pe->sata_link_rates & 0xF0) >> 4;
9572908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].min_sata_lrate =
9582908d778SJames Bottomley 			(pe->sata_link_rates & 0x0F);
9592908d778SJames Bottomley 		asd_ha->hw_prof.phy_desc[i].flags = pe->flags;
9602908d778SJames Bottomley 		ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x,"
9612908d778SJames Bottomley 			    " sata rate:0x%x-0x%x, flags:0x%x\n",
9622908d778SJames Bottomley 			    i,
9632908d778SJames Bottomley 			    SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr),
9642908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].max_sas_lrate,
9652908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].min_sas_lrate,
9662908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].max_sata_lrate,
9672908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].min_sata_lrate,
9682908d778SJames Bottomley 			    asd_ha->hw_prof.phy_desc[i].flags);
9692908d778SJames Bottomley 	}
9702908d778SJames Bottomley 
9712908d778SJames Bottomley 	return 0;
9722908d778SJames Bottomley }
9732908d778SJames Bottomley 
9742908d778SJames Bottomley /**
9752908d778SJames Bottomley  * asd_process_ctrl_a_user - process CTRL-A user settings
9762908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
9772908d778SJames Bottomley  * @flash_dir: pointer to the flash directory
9782908d778SJames Bottomley  */
9792908d778SJames Bottomley static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
9802908d778SJames Bottomley 				   struct asd_flash_dir *flash_dir)
9812908d778SJames Bottomley {
9822908d778SJames Bottomley 	int err, i;
9832908d778SJames Bottomley 	u32 offs, size;
9842908d778SJames Bottomley 	struct asd_ll_el *el;
9852908d778SJames Bottomley 	struct asd_ctrla_phy_settings *ps;
9862908d778SJames Bottomley 	struct asd_ctrla_phy_settings dflt_ps;
9872908d778SJames Bottomley 
9882908d778SJames Bottomley 	err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size);
9892908d778SJames Bottomley 	if (err) {
9902908d778SJames Bottomley 		ASD_DPRINTK("couldn't find CTRL-A user settings section\n");
9912908d778SJames Bottomley 		ASD_DPRINTK("Creating default CTRL-A user settings section\n");
9922908d778SJames Bottomley 
9932908d778SJames Bottomley 		dflt_ps.id0 = 'h';
9942908d778SJames Bottomley 		dflt_ps.num_phys = 8;
9952908d778SJames Bottomley 		for (i =0; i < ASD_MAX_PHYS; i++) {
9962908d778SJames Bottomley 			memcpy(dflt_ps.phy_ent[i].sas_addr,
9972908d778SJames Bottomley 			       asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
9982908d778SJames Bottomley 			dflt_ps.phy_ent[i].sas_link_rates = 0x98;
9992908d778SJames Bottomley 			dflt_ps.phy_ent[i].flags = 0x0;
10002908d778SJames Bottomley 			dflt_ps.phy_ent[i].sata_link_rates = 0x0;
10012908d778SJames Bottomley 		}
10022908d778SJames Bottomley 
10032908d778SJames Bottomley 		size = sizeof(struct asd_ctrla_phy_settings);
10042908d778SJames Bottomley 		ps = &dflt_ps;
10052908d778SJames Bottomley 	}
10062908d778SJames Bottomley 
10072908d778SJames Bottomley 	if (size == 0)
10082908d778SJames Bottomley 		goto out;
10092908d778SJames Bottomley 
10102908d778SJames Bottomley 	err = -ENOMEM;
10112908d778SJames Bottomley 	el = kmalloc(size, GFP_KERNEL);
10122908d778SJames Bottomley 	if (!el) {
10132908d778SJames Bottomley 		ASD_DPRINTK("no mem for ctrla user settings section\n");
10142908d778SJames Bottomley 		goto out;
10152908d778SJames Bottomley 	}
10162908d778SJames Bottomley 
10172908d778SJames Bottomley 	err = asd_read_flash_seg(asd_ha, (void *)el, offs, size);
10182908d778SJames Bottomley 	if (err) {
10192908d778SJames Bottomley 		ASD_DPRINTK("couldn't read ctrla phy settings section\n");
10202908d778SJames Bottomley 		goto out2;
10212908d778SJames Bottomley 	}
10222908d778SJames Bottomley 
10232908d778SJames Bottomley 	err = -ENOENT;
10242908d778SJames Bottomley 	ps = asd_find_ll_by_id(el, 'h', 0xFF);
10252908d778SJames Bottomley 	if (!ps) {
10262908d778SJames Bottomley 		ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
10272908d778SJames Bottomley 		goto out2;
10282908d778SJames Bottomley 	}
10292908d778SJames Bottomley 
10302908d778SJames Bottomley 	err = asd_process_ctrla_phy_settings(asd_ha, ps);
10312908d778SJames Bottomley 	if (err) {
10322908d778SJames Bottomley 		ASD_DPRINTK("couldn't process ctrla phy settings\n");
10332908d778SJames Bottomley 		goto out2;
10342908d778SJames Bottomley 	}
10352908d778SJames Bottomley out2:
10362908d778SJames Bottomley 	kfree(el);
10372908d778SJames Bottomley out:
10382908d778SJames Bottomley 	return err;
10392908d778SJames Bottomley }
10402908d778SJames Bottomley 
10412908d778SJames Bottomley /**
10422908d778SJames Bottomley  * asd_read_flash - read flash memory
10432908d778SJames Bottomley  * @asd_ha: pointer to the host adapter structure
10442908d778SJames Bottomley  */
10452908d778SJames Bottomley int asd_read_flash(struct asd_ha_struct *asd_ha)
10462908d778SJames Bottomley {
10472908d778SJames Bottomley 	int err;
10482908d778SJames Bottomley 	struct asd_flash_dir *flash_dir;
10492908d778SJames Bottomley 
10502908d778SJames Bottomley 	err = asd_flash_getid(asd_ha);
10512908d778SJames Bottomley 	if (err)
10522908d778SJames Bottomley 		return err;
10532908d778SJames Bottomley 
10542908d778SJames Bottomley 	flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL);
10552908d778SJames Bottomley 	if (!flash_dir)
10562908d778SJames Bottomley 		return -ENOMEM;
10572908d778SJames Bottomley 
10582908d778SJames Bottomley 	err = -ENOENT;
10592908d778SJames Bottomley 	if (!asd_find_flash_dir(asd_ha, flash_dir)) {
10602908d778SJames Bottomley 		ASD_DPRINTK("couldn't find flash directory\n");
10612908d778SJames Bottomley 		goto out;
10622908d778SJames Bottomley 	}
10632908d778SJames Bottomley 
10642908d778SJames Bottomley 	if (le32_to_cpu(flash_dir->rev) != 2) {
10652908d778SJames Bottomley 		asd_printk("unsupported flash dir version:0x%x\n",
10662908d778SJames Bottomley 			   le32_to_cpu(flash_dir->rev));
10672908d778SJames Bottomley 		goto out;
10682908d778SJames Bottomley 	}
10692908d778SJames Bottomley 
10702908d778SJames Bottomley 	err = asd_process_ms(asd_ha, flash_dir);
10712908d778SJames Bottomley 	if (err) {
10722908d778SJames Bottomley 		ASD_DPRINTK("couldn't process manuf sector settings\n");
10732908d778SJames Bottomley 		goto out;
10742908d778SJames Bottomley 	}
10752908d778SJames Bottomley 
10762908d778SJames Bottomley 	err = asd_process_ctrl_a_user(asd_ha, flash_dir);
10772908d778SJames Bottomley 	if (err) {
10782908d778SJames Bottomley 		ASD_DPRINTK("couldn't process CTRL-A user settings\n");
10792908d778SJames Bottomley 		goto out;
10802908d778SJames Bottomley 	}
10812908d778SJames Bottomley 
10822908d778SJames Bottomley out:
10832908d778SJames Bottomley 	kfree(flash_dir);
10842908d778SJames Bottomley 	return err;
10852908d778SJames Bottomley }
1086