aic94xx: aic94x_sds.c Shared Data Structures and memory
PATCH 15/20 aic94xx: aic94x_sds.c Shared Data Structures and memory
Signed-off-by Luben Tuikov <[email protected]>
drivers/scsi/aic94xx/aic94xx_sds.c +964 -0
@@ -0,0 +1,964 @@ +/* +* Aic94xx SAS/SATA driver access to shared data structures and memory +* maps. +* +* Copyright (C) 2005 Adaptec, Inc. All rights reserved. +* Copyright (C) 2005 Luben Tuikov <[email protected]> +* +* This file is licensed under GPLv2. +* +* This file is part of the aic94xx driver. +* +* The aic94xx driver is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; version 2 of the +* License. +* +* The aic94xx driver is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the aic94xx driver; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +* $Id: //depot/aic94xx/aic94xx_sds.c#34 $ +*/ + +#include <linux/pci.h> +#include <linux/delay.h> + +#include "aic94xx.h" +#include "aic94xx_reg.h" + +/* ---------- OCM stuff ---------- */ + +struct asd_ocm_dir_ent { +u8 type; +u8 offs[3 ]; +u8 _r1; +u8 size[3 ]; +} __attribute__ ((packed)); + +struct asd_ocm_dir { +char sig[2 ]; +u8 _r1[2 ]; +u8 major;/* 0 */ +u8 minor;/* 0 */ +u8 _r2; +u8 num_de; +struct asd_ocm_dir_ent entry[15 ]; +} __attribute__ ((packed)); + +struct asd_bios_chim_struct { +char sig[4 ]; +u8 major;/* 1 */ +u8 minor;/* 0 */ +u8 bios_major; +u8 bios_minor; +__le32 bios_build; +u8 flags; +u8 pci_slot; +__le16 ue_num; +__le16 ue_size; +u8 _r[14 ]; +/* The unit element array is right here. +*/ +} __attribute__ ((packed)); + +/** +* asd_read_ocm_seg - read an on chip memory (OCM) segment +* @asd_ha: pointer to the host adapter structure +* @buffer: where to write the read data +* @offs: offset into OCM where to read from +* @size: how many bytes to read +* +* Return the number of bytes not read. Return 0 on success. +*/ +static int asd_read_ocm_seg (struct asd_ha_struct *asd_ha,void *buffer, +u32 offs,int size) +{ +u8 *p = buffer; +if (unlikely (asd_ha->iospace)) +asd_read_reg_string (asd_ha, buffer, offs+OCM_BASE_ADDR , size); +else { +for ( ; size >0 ; size--, offs++, p++) + *p =asd_read_ocm_byte (asd_ha, offs); + } +return size; +} + +static int asd_read_ocm_dir (struct asd_ha_struct *asd_ha, +struct asd_ocm_dir *dir,u32 offs) +{ +int err =asd_read_ocm_seg (asd_ha, (void *)dir, offs,sizeof (*dir)); +if (err) { +ASD_DPRINTK ("couldn't read ocm segment\n" ); +return err; + } +if (dir->sig[0 ] !='M' || dir->sig[1 ] !='O' ) { +ASD_DPRINTK ("no valid dir signature(%c%c) at start of OCM\n" , + dir->sig[0 ], dir->sig[1 ]); +return -ENOENT ; + } +if (dir->major !=0 ) { +asd_printk ("unsupported major version of ocm dir:0x%x\n" , + dir->major); +return -ENOENT ; + } + dir->num_de &=0xf ; +return 0 ; +} + +#define THREE_TO_NUM (X ) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16)) + +static int asd_find_dir_entry (struct asd_ocm_dir *dir,u8 type, +u32 *offs,u32 *size) +{ +int i; +struct asd_ocm_dir_ent *ent; + +for (i =0 ; i < dir->num_de; i++) { +if (dir->entry[i].type == type) +break ; + } +if (i >= dir->num_de) +return -ENOENT ; + ent = &dir->entry[i]; + *offs = (u32 )THREE_TO_NUM (ent->offs); + *size = (u32 )THREE_TO_NUM (ent->size); +return 0 ; +} + +#define OCM_BIOS_CHIM_DE 2 +#define BC_BIOS_PRESENT 1 + +static int asd_get_bios_chim (struct asd_ha_struct *asd_ha, +struct asd_ocm_dir *dir) +{ +int err; +struct asd_bios_chim_struct *bc_struct; +u32 offs, size; + + err =asd_find_dir_entry (dir,OCM_BIOS_CHIM_DE , &offs, &size); +if (err) { +ASD_DPRINTK ("couldn't find BIOS_CHIM dir ent\n" ); + goto out; + } + err = -ENOMEM ; + bc_struct =kmalloc (sizeof (*bc_struct),GFP_KERNEL ); +if (!bc_struct) { +asd_printk ("no memory for bios_chim struct\n" ); + goto out; + } + err =asd_read_ocm_seg (asd_ha, (void *)bc_struct, offs, +sizeof (*bc_struct)); +if (err) { +ASD_DPRINTK ("couldn't read ocm segment\n" ); + goto out2; + } +if (strncmp (bc_struct->sig,"SOIB" ,4 ) + &&strncmp (bc_struct->sig,"IPSA" ,4 )) { +ASD_DPRINTK ("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n" , + bc_struct->sig[0 ], bc_struct->sig[1 ], + bc_struct->sig[2 ], bc_struct->sig[3 ]); + err = -ENOENT ; + goto out2; + } +if (bc_struct->major !=1 ) { +asd_printk ("BIOS_CHIM unsupported major version:0x%x\n" , + bc_struct->major); + err = -ENOENT ; + goto out2; + } +if (bc_struct->flags &BC_BIOS_PRESENT ) { + asd_ha->hw_prof.bios.present =1 ; + asd_ha->hw_prof.bios.maj = bc_struct->bios_major; + asd_ha->hw_prof.bios.min = bc_struct->bios_minor; + asd_ha->hw_prof.bios.bld =le32_to_cpu (bc_struct->bios_build); +ASD_DPRINTK ("BIOS present (%d,%d), %d\n" , + asd_ha->hw_prof.bios.maj, + asd_ha->hw_prof.bios.min, + asd_ha->hw_prof.bios.bld); + } + asd_ha->hw_prof.ue.num =le16_to_cpu (bc_struct->ue_num); + asd_ha->hw_prof.ue.size=le16_to_cpu (bc_struct->ue_size); +ASD_DPRINTK ("ue num:%d, ue size:%d\n" , asd_ha->hw_prof.ue.num, + asd_ha->hw_prof.ue.size); + size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size; +if (size >0 ) { + err = -ENOMEM ; + asd_ha->hw_prof.ue.area =kmalloc (size,GFP_KERNEL ); +if (!asd_ha->hw_prof.ue.area) + goto out2; + err =asd_read_ocm_seg (asd_ha, (void *)asd_ha->hw_prof.ue.area, + offs +sizeof (*bc_struct), size); +if (err) { +kfree (asd_ha->hw_prof.ue.area); + asd_ha->hw_prof.ue.area =NULL ; + asd_ha->hw_prof.ue.num =0 ; + asd_ha->hw_prof.ue.size =0 ; +ASD_DPRINTK ("couldn't read ue entries(%d)\n" , err); + } + } +out2: +kfree (bc_struct); +out: +return err; +} + +/** +* asd_read_ocm - read on chip memory (OCM) +* @asd_ha: pointer to the host adapter structure +*/ +int asd_read_ocm (struct asd_ha_struct *asd_ha) +{ +int err; +struct asd_ocm_dir *dir; + + dir =kmalloc (sizeof (*dir),GFP_KERNEL ); +if (!dir) { +asd_printk ("no memory for ocm dir\n" ); +return -ENOMEM ; + } + + err =asd_read_ocm_dir (asd_ha, dir,0 ); +if (err) + goto out; + + err =asd_get_bios_chim (asd_ha, dir); +out: +kfree (dir); +return err; +} + +/* ---------- FLASH stuff ---------- */ + +#define FLASH_RESET 0xF0 +#define FLASH_MANUF_AMD 1 + +#define FLASH_SIZE 0x200000 +#define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " +#define FLASH_NEXT_ENTRY_OFFS 0x2000 +#define FLASH_MAX_DIR_ENTRIES 32 + +#define FLASH_DE_TYPE_MASK 0x3FFFFFFF +#define FLASH_DE_MS 0x120 +#define FLASH_DE_CTRL_A_USER 0xE0 + +struct asd_flash_de { +__le32 type; +__le32 offs; +__le32 pad_size; +__le32 image_size; +__le32 chksum; +u8 _r[12 ]; +u8 version[32 ]; +} __attribute__ ((packed)); + +struct asd_flash_dir { +u8 cookie[32 ]; +__le32 rev;/* 2 */ +__le32 chksum; +__le32 chksum_antidote; +__le32 bld; +u8 bld_id[32 ];/* build id data */ +u8 ver_data[32 ];/* date and time of build */ +__le32 ae_mask; +__le32 v_mask; +__le32 oc_mask; +u8 _r[20 ]; +struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES ]; +} __attribute__ ((packed)); + +struct asd_manuf_sec { +char sig[2 ];/* 'S', 'M' */ +u16 offs_next; +u8 maj;/* 0 */ +u8 min;/* 0 */ +u16 chksum; +u16 size; +u8 _r[6 ]; +u8 sas_addr[SAS_ADDR_SIZE ]; +u8 pcba_sn[ASD_PCBA_SN_SIZE ]; +/* Here start the other segments */ +u8 linked_list[0 ]; +} __attribute__ ((packed)); + +struct asd_manuf_phy_desc { +u8 state;/* low 4 bits */ +#define MS_PHY_STATE_ENABLEABLE 0 +#define MS_PHY_STATE_REPORTED 1 +#define MS_PHY_STATE_HIDDEN 2 +u8 phy_id; +u16 _r; +u8 phy_control_0;/* mode 5 reg 0x160 */ +u8 phy_control_1;/* mode 5 reg 0x161 */ +u8 phy_control_2;/* mode 5 reg 0x162 */ +u8 phy_control_3;/* mode 5 reg 0x163 */ +} __attribute__ ((packed)); + +struct asd_manuf_phy_param { +char sig[2 ];/* 'P', 'M' */ +u16 next; +u8 maj;/* 0 */ +u8 min;/* 2 */ +u8 num_phy_desc;/* 8 */ +u8 phy_desc_size;/* 8 */ +u8 _r[3 ]; +u8 usage_model_id; +u32 _r2; +struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS ]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_sb_type[] = { +"unknown" , +"SGPIO" , + [2 ...0x7F ] ="unknown" , + [0x80 ] ="ADPT_I2C" , + [0x81 ...0xFF ] ="VENDOR_UNIQUExx" +}; +#endif + +struct asd_ms_sb_desc { +u8 type; +u8 node_desc_index; +u8 conn_desc_index; +u8 _recvd[0 ]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_conn_type[] = { + [0 ...7 ] ="unknown" , +"SFF8470" , +"SFF8482" , +"SFF8484" , + [0x80 ] ="PCIX_DAUGHTER0" , + [0x81 ] ="SAS_DAUGHTER0" , + [0x82 ...0xFF ] ="VENDOR_UNIQUExx" +}; + +static const char *asd_conn_location[] = { +"unknown" , +"internal" , +"external" , +"board_to_board" , +}; +#endif + +struct asd_ms_conn_desc { +u8 type; +u8 location; +u8 num_sideband_desc; +u8 size_sideband_desc; +u32 _resvd; +u8 name[16 ]; +struct asd_ms_sb_desc sb_desc[0 ]; +} __attribute__ ((packed)); + +struct asd_nd_phy_desc { +u8 vp_attch_type; +u8 attch_specific[0 ]; +} __attribute__ ((packed)); + +#if 0 +static const char *asd_node_type[] = { +"IOP" , +"IO_CONTROLLER" , +"EXPANDER" , +"PORT_MULTIPLIER" , +"PORT_MULTIPLEXER" , +"MULTI_DROP_I2C_BUS" , +}; +#endif + +struct asd_ms_node_desc { +u8 type; +u8 num_phy_desc; +u8 size_phy_desc; +u8 _resvd; +u8 name[16 ]; +struct asd_nd_phy_desc phy_desc[0 ]; +} __attribute__ ((packed)); + +struct asd_ms_conn_map { +char sig[2 ];/* 'M', 'C' */ +__le16 next; +u8 maj;/* 0 */ +u8 min;/* 0 */ +__le16 cm_size;/* size of this struct */ +u8 num_conn; +u8 conn_size; +u8 num_nodes; +u8 usage_model_id; +u32 _resvd; +struct asd_ms_conn_desc conn_desc[0 ]; +struct asd_ms_node_desc node_desc[0 ]; +} __attribute__ ((packed)); + +struct asd_ctrla_phy_entry { +u8 sas_addr[SAS_ADDR_SIZE ]; +u8 sas_link_rates;/* max in hi bits, min in low bits */ +u8 flags; +u8 sata_link_rates; +u8 _r[5 ]; +} __attribute__ ((packed)); + +struct asd_ctrla_phy_settings { +u8 id0;/* P'h'y */ +u8 _r; +u16 next; +u8 num_phys;/* number of PHYs in the PCI function */ +u8 _r2[3 ]; +struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS ]; +} __attribute__ ((packed)); + +struct asd_ll_el { +u8 id0; +u8 id1; +__le16 next; +u8 something_here[0 ]; +} __attribute__ ((packed)); + +static int asd_poll_flash (struct asd_ha_struct *asd_ha) +{ +int c; +u8 d; + +for (c =5000 ; c >0 ; c--) { + d =asd_read_reg_byte (asd_ha, asd_ha->hw_prof.flash.bar); + d ^=asd_read_reg_byte (asd_ha, asd_ha->hw_prof.flash.bar); +if (!d) +return 0 ; +udelay (5 ); + } +return -ENOENT ; +} + +static int asd_reset_flash (struct asd_ha_struct *asd_ha) +{ +int err; + + err =asd_poll_flash (asd_ha); +if (err) +return err; +asd_write_reg_byte (asd_ha, asd_ha->hw_prof.flash.bar,FLASH_RESET ); + err =asd_poll_flash (asd_ha); + +return err; +} + +static inline int asd_read_flash_seg (struct asd_ha_struct *asd_ha, +void *buffer,u32 offs,int size) +{ +asd_read_reg_string (asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs, + size); +return 0 ; +} + +/** +* asd_find_flash_dir - finds and reads the flash directory +* @asd_ha: pointer to the host adapter structure +* @flash_dir: pointer to flash directory structure +* +* If found, the flash directory segment will be copied to +* @flash_dir. Return 1 if found, 0 if not. +*/ +static int asd_find_flash_dir (struct asd_ha_struct *asd_ha, +struct asd_flash_dir *flash_dir) +{ +u32 v; +for (v =0 ; v <FLASH_SIZE ; v +=FLASH_NEXT_ENTRY_OFFS ) { +asd_read_flash_seg (asd_ha, flash_dir, v, +sizeof (FLASH_DIR_COOKIE )-1 ); +if (memcmp (flash_dir->cookie,FLASH_DIR_COOKIE , +sizeof (FLASH_DIR_COOKIE )-1 ) ==0 ) { + asd_ha->hw_prof.flash.dir_offs = v; +asd_read_flash_seg (asd_ha, flash_dir, v, +sizeof (*flash_dir)); +return 1 ; + } + } +return 0 ; +} + +static int asd_flash_getid (struct asd_ha_struct *asd_ha) +{ +int err =0 ; +u32 reg, inc; + + reg =asd_read_reg_dword (asd_ha,EXSICNFGR ); + +if (!(reg &FLASHEX )) { +ASD_DPRINTK ("flash doesn't exist\n" ); +return -ENOENT ; + } +if (pci_read_config_dword (asd_ha->pcidev,PCI_CONF_FLSH_BAR , + &asd_ha->hw_prof.flash.bar)) { +asd_printk ("couldn't read PCI_CONF_FLSH_BAR of %s\n" , +pci_name (asd_ha->pcidev)); +return -ENOENT ; + } + asd_ha->hw_prof.flash.present =1 ; + asd_ha->hw_prof.flash.wide = reg &FLASHW ?1 :0 ; + err =asd_reset_flash (asd_ha); +if (err) { +ASD_DPRINTK ("couldn't reset flash(%d)\n" , err); +return err; + } +/* Get flash info. This would most likely be AMD Am29LV family flash. +* First try the sequence for word mode. It is the same as for +* 008B (byte mode only), 160B (word mode) and 800D (word mode). +*/ + reg = asd_ha->hw_prof.flash.bar; + inc = asd_ha->hw_prof.flash.wide ?2 :1 ; +asd_write_reg_byte (asd_ha, reg +0x555 ,0xAA ); +asd_write_reg_byte (asd_ha, reg +0x2AA ,0x55 ); +asd_write_reg_byte (asd_ha, reg +0x555 ,0x90 ); + asd_ha->hw_prof.flash.manuf =asd_read_reg_byte (asd_ha, reg); + asd_ha->hw_prof.flash.dev_id=asd_read_reg_byte (asd_ha,reg+inc); + asd_ha->hw_prof.flash.sec_prot =asd_read_reg_byte (asd_ha,reg+inc+inc); +/* Get out of autoselect mode. */ + err =asd_reset_flash (asd_ha); + +if (asd_ha->hw_prof.flash.manuf ==FLASH_MANUF_AMD ) { +ASD_DPRINTK ("0Found FLASH(%d) manuf:%d, dev_id:0x%x, " +"sec_prot:%d\n" , + asd_ha->hw_prof.flash.wide ?16 :8 , + asd_ha->hw_prof.flash.manuf, + asd_ha->hw_prof.flash.dev_id, + asd_ha->hw_prof.flash.sec_prot); +return 0 ; + } + +/* Ok, try the sequence for byte mode of 160B and 800D. +* We may actually never need this. +*/ +asd_write_reg_byte (asd_ha, reg +0xAAA ,0xAA ); +asd_write_reg_byte (asd_ha, reg +0x555 ,0x55 ); +asd_write_reg_byte (asd_ha, reg +0xAAA ,0x90 ); + asd_ha->hw_prof.flash.manuf =asd_read_reg_byte (asd_ha, reg); + asd_ha->hw_prof.flash.dev_id =asd_read_reg_byte (asd_ha, reg +2 ); + asd_ha->hw_prof.flash.sec_prot =asd_read_reg_byte (asd_ha, reg +4 ); + err =asd_reset_flash (asd_ha); + +if (asd_ha->hw_prof.flash.manuf ==FLASH_MANUF_AMD ) { +ASD_DPRINTK ("1Found FLASH(%d) manuf:%d, dev_id:0x%x, " +"sec_prot:%d\n" , + asd_ha->hw_prof.flash.wide ?16 :8 , + asd_ha->hw_prof.flash.manuf, + asd_ha->hw_prof.flash.dev_id, + asd_ha->hw_prof.flash.sec_prot); +return 0 ; + } + +return -ENOENT ; +} + +static u16 asd_calc_flash_chksum (u16 *p,int size) +{ +u16 chksum =0 ; + +while (size-- >0 ) + chksum += *p++; + +return chksum; +} + + +static int asd_find_flash_de (struct asd_flash_dir *flash_dir,u32 entry_type, +u32 *offs,u32 *size) +{ +int i; +struct asd_flash_de *de; + +for (i =0 ; i <FLASH_MAX_DIR_ENTRIES ; i++) { +u32 type =le32_to_cpu (flash_dir->dir_entry[i].type); + + type &=FLASH_DE_TYPE_MASK ; +if (type == entry_type) +break ; + } +if (i >=FLASH_MAX_DIR_ENTRIES ) +return -ENOENT ; + de = &flash_dir->dir_entry[i]; + *offs =le32_to_cpu (de->offs); + *size =le32_to_cpu (de->pad_size); +return 0 ; +} + +static int asd_validate_ms (struct asd_manuf_sec *ms) +{ +if (ms->sig[0 ] !='S' || ms->sig[1 ] !='M' ) { +ASD_DPRINTK ("manuf sec: no valid sig(%c%c)\n" , + ms->sig[0 ], ms->sig[1 ]); +return -ENOENT ; + } +if (ms->maj !=0 ) { +asd_printk ("unsupported manuf. sector. major version:%x\n" , + ms->maj); +return -ENOENT ; + } + ms->offs_next =le16_to_cpu ((__force __le16 ) ms->offs_next); + ms->chksum =le16_to_cpu ((__force __le16 ) ms->chksum); + ms->size =le16_to_cpu ((__force __le16 ) ms->size); + +if (asd_calc_flash_chksum ((u16 *)ms, ms->size/2 )) { +ASD_DPRINTK ("failed manuf sector checksum\n" ); +return -EINVAL ; + } +return 0 ; +} + +static int asd_ms_get_sas_addr (struct asd_ha_struct *asd_ha, +struct asd_manuf_sec *ms) +{ +memcpy (asd_ha->hw_prof.sas_addr, ms->sas_addr,SAS_ADDR_SIZE ); +return 0 ; +} + +static int asd_ms_get_pcba_sn (struct asd_ha_struct *asd_ha, +struct asd_manuf_sec *ms) +{ +memcpy (asd_ha->hw_prof.pcba_sn, ms->pcba_sn,ASD_PCBA_SN_SIZE ); + asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE ] ='\0' ; +return 0 ; +} + +/** +* asd_find_ll_by_id - find a linked list entry by its id +* @start: void pointer to the first element in the linked list +* @id0: the first byte of the id (offs 0) +* @id1: the second byte of the id (offs 1) +* +* @start has to be the _base_ element start, since the +* linked list entries's offset is from this pointer. +* Some linked list entries use only the first id, in which case +* you can pass 0xFF for the second. +*/ +static void *asd_find_ll_by_id (void *const start,const u8 id0,const u8 id1) +{ +struct asd_ll_el *el = start; + +do { +switch (id1) { +default : +if (el->id1 == id1) +case 0xFF : +if (el->id0 == id0) +return el; + } + el = start +le16_to_cpu (el->next); + }while (el != start); + +return NULL ; +} + +/** +* asd_ms_get_phy_params - get phy parameters from the manufacturing sector +* @asd_ha: pointer to the host adapter structure +* @manuf_sec: pointer to the manufacturing sector +* +* The manufacturing sector contans also the linked list of sub-segments, +* since when it was read, its size was taken from the flash directory, +* not from the structure size. +* +* HIDDEN phys do not count in the total count. REPORTED phys cannot +* be enabled but are reported and counted towards the total. +* ENEBLEABLE phys are enabled by default and count towards the total. +* The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys +* merely specifies the number of phys the host adapter decided to +* report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, +* phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE. +* In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 +* are actually enabled (enabled by default, max number of phys +* enableable in this case). +*/ +static int asd_ms_get_phy_params (struct asd_ha_struct *asd_ha, +struct asd_manuf_sec *manuf_sec) +{ +int i; +int en_phys =0 ; +int rep_phys =0 ; +struct asd_manuf_phy_param *phy_param; + + phy_param =asd_find_ll_by_id (manuf_sec,'P' ,'M' ); +if (!phy_param) { +ASD_DPRINTK ("ms: no phy parameters found\n" ); +return -ENOENT ; + } + +if (phy_param->maj !=0 ) { +asd_printk ("unsupported manuf. phy param major version:0x%x\n" , + phy_param->maj); +return -ENOENT ; + } + +ASD_DPRINTK ("ms: num_phy_desc: %d\n" , phy_param->num_phy_desc); + asd_ha->hw_prof.enabled_phys =0 ; +for (i =0 ; i < phy_param->num_phy_desc; i++) { +struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; +switch (pd->state &0xF ) { +case MS_PHY_STATE_HIDDEN : +ASD_DPRINTK ("ms: phy%d: HIDDEN\n" , i); +continue ; +case MS_PHY_STATE_REPORTED : +ASD_DPRINTK ("ms: phy%d: REPORTED\n" , i); + asd_ha->hw_prof.enabled_phys &= ~(1 << i); + rep_phys++; +continue ; +case MS_PHY_STATE_ENABLEABLE : +ASD_DPRINTK ("ms: phy%d: ENEBLEABLE\n" , i); + asd_ha->hw_prof.enabled_phys |= (1 << i); + en_phys++; +break ; + } + asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; + asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; + asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; + asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; + } + asd_ha->hw_prof.max_phys = rep_phys + en_phys; + asd_ha->hw_prof.num_phys = en_phys; +ASD_DPRINTK ("ms: max_phys:0x%x, num_phys:0x%x\n" , + asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); +ASD_DPRINTK ("ms: enabled_phys:0x%x\n" , asd_ha->hw_prof.enabled_phys); +return 0 ; +} + +static int asd_ms_get_connector_map (struct asd_ha_struct *asd_ha, +struct asd_manuf_sec *manuf_sec) +{ +struct asd_ms_conn_map *cm; + + cm =asd_find_ll_by_id (manuf_sec,'M' ,'C' ); +if (!cm) { +ASD_DPRINTK ("ms: no connector map found\n" ); +return -ENOENT ; + } + +if (cm->maj !=0 ) { +ASD_DPRINTK ("ms: unsupported: connector map major version 0x%x" +"\n" , cm->maj); +return -ENOENT ; + } + +/* XXX */ + +return 0 ; +} + + +/** +* asd_process_ms - find and extract information from the manufacturing sector +* @asd_ha: pointer to the host adapter structure +* @flash_dir: pointer to the flash directory +*/ +static int asd_process_ms (struct asd_ha_struct *asd_ha, +struct asd_flash_dir *flash_dir) +{ +int err; +struct asd_manuf_sec *manuf_sec; +u32 offs, size; + + err =asd_find_flash_de (flash_dir,FLASH_DE_MS , &offs, &size); +if (err) { +ASD_DPRINTK ("Couldn't find the manuf. sector\n" ); + goto out; + } + +if (size ==0 ) + goto out; + + err = -ENOMEM ; + manuf_sec =kmalloc (size,GFP_KERNEL ); +if (!manuf_sec) { +ASD_DPRINTK ("no mem for manuf sector\n" ); + goto out; + } + + err =asd_read_flash_seg (asd_ha, (void *)manuf_sec, offs, size); +if (err) { +ASD_DPRINTK ("couldn't read manuf sector at 0x%x, size 0x%x\n" , + offs, size); + goto out2; + } + + err =asd_validate_ms (manuf_sec); +if (err) { +ASD_DPRINTK ("couldn't validate manuf sector\n" ); + goto out2; + } + + err =asd_ms_get_sas_addr (asd_ha, manuf_sec); +if (err) { +ASD_DPRINTK ("couldn't read the SAS_ADDR\n" ); + goto out2; + } +ASD_DPRINTK ("manuf sect SAS_ADDR %llx\n" , +SAS_ADDR (asd_ha->hw_prof.sas_addr)); + + err =asd_ms_get_pcba_sn (asd_ha, manuf_sec); +if (err) { +ASD_DPRINTK ("couldn't read the PCBA SN\n" ); + goto out2; + } +ASD_DPRINTK ("manuf sect PCBA SN %s\n" , asd_ha->hw_prof.pcba_sn); + + err =asd_ms_get_phy_params (asd_ha, manuf_sec); +if (err) { +ASD_DPRINTK ("ms: couldn't get phy parameters\n" ); + goto out2; + } + + err =asd_ms_get_connector_map (asd_ha, manuf_sec); +if (err) { +ASD_DPRINTK ("ms: couldn't get connector map\n" ); + goto out2; + } + +out2: +kfree (manuf_sec); +out: +return err; +} + +static int asd_process_ctrla_phy_settings (struct asd_ha_struct *asd_ha, +struct asd_ctrla_phy_settings *ps) +{ +int i; +for (i =0 ; i < ps->num_phys; i++) { +struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; + +if (!PHY_ENABLED (asd_ha, i)) +continue ; +if (*(u64 *)pe->sas_addr ==0 ) { + asd_ha->hw_prof.enabled_phys &= ~(1 << i); +continue ; + } +/* This is the SAS address which should be sent in IDENTIFY. */ +memcpy (asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, +SAS_ADDR_SIZE ); + asd_ha->hw_prof.phy_desc[i].max_sas_lrate = + (pe->sas_link_rates &0xF0 ) >>4 ; + asd_ha->hw_prof.phy_desc[i].min_sas_lrate = + (pe->sas_link_rates &0x0F ); + asd_ha->hw_prof.phy_desc[i].max_sata_lrate = + (pe->sata_link_rates &0xF0 ) >>4 ; + asd_ha->hw_prof.phy_desc[i].min_sata_lrate = + (pe->sata_link_rates &0x0F ); + asd_ha->hw_prof.phy_desc[i].flags = pe->flags; +ASD_DPRINTK ("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," +" sata rate:0x%x-0x%x, flags:0x%x\n" , + i, +SAS_ADDR (asd_ha->hw_prof.phy_desc[i].sas_addr), + asd_ha->hw_prof.phy_desc[i].max_sas_lrate, + asd_ha->hw_prof.phy_desc[i].min_sas_lrate, + asd_ha->hw_prof.phy_desc[i].max_sata_lrate, + asd_ha->hw_prof.phy_desc[i].min_sata_lrate, + asd_ha->hw_prof.phy_desc[i].flags); + } + +return 0 ; +} + +/** +* asd_process_ctrl_a_user - process CTRL-A user settings +* @asd_ha: pointer to the host adapter structure +* @flash_dir: pointer to the flash directory +*/ +static int asd_process_ctrl_a_user (struct asd_ha_struct *asd_ha, +struct asd_flash_dir *flash_dir) +{ +int err; +u32 offs, size; +struct asd_ll_el *el; +struct asd_ctrla_phy_settings *ps; + + err =asd_find_flash_de (flash_dir,FLASH_DE_CTRL_A_USER , &offs, &size); +if (err) { +ASD_DPRINTK ("couldn't find CTRL-A user settings section\n" ); + goto out; + } + +if (size ==0 ) + goto out; + + err = -ENOMEM ; + el =kmalloc (size,GFP_KERNEL ); +if (!el) { +ASD_DPRINTK ("no mem for ctrla user settings section\n" ); + goto out; + } + + err =asd_read_flash_seg (asd_ha, (void *)el, offs, size); +if (err) { +ASD_DPRINTK ("couldn't read ctrla phy settings section\n" ); + goto out2; + } + + err = -ENOENT ; + ps =asd_find_ll_by_id (el,'h' ,0xFF ); +if (!ps) { +ASD_DPRINTK ("couldn't find ctrla phy settings struct\n" ); + goto out2; + } + + err =asd_process_ctrla_phy_settings (asd_ha, ps); +if (err) { +ASD_DPRINTK ("couldn't process ctrla phy settings\n" ); + goto out2; + } +out2: +kfree (el); +out: +return err; +} + +/** +* asd_read_flash - read flash memory +* @asd_ha: pointer to the host adapter structure +*/ +int asd_read_flash (struct asd_ha_struct *asd_ha) +{ +int err; +struct asd_flash_dir *flash_dir; + + err =asd_flash_getid (asd_ha); +if (err) +return err; + + flash_dir =kmalloc (sizeof (*flash_dir),GFP_KERNEL ); +if (!flash_dir) +return -ENOMEM ; + + err = -ENOENT ; +if (!asd_find_flash_dir (asd_ha, flash_dir)) { +ASD_DPRINTK ("couldn't find flash directory\n" ); + goto out; + } + +if (le32_to_cpu (flash_dir->rev) !=2 ) { +asd_printk ("unsupported flash dir version:0x%x\n" , +le32_to_cpu (flash_dir->rev)); + goto out; + } + + err =asd_process_ms (asd_ha, flash_dir); +if (err) { +ASD_DPRINTK ("couldn't process manuf sector settings\n" ); + goto out; + } + + err =asd_process_ctrl_a_user (asd_ha, flash_dir); +if (err) { +ASD_DPRINTK ("couldn't process CTRL-A user settings\n" ); + goto out; + } + +out: +kfree (flash_dir); +return err; +}