18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Aic94xx SAS/SATA driver access to shared data structures and memory 48c2ecf20Sopenharmony_ci * maps. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "aic94xx.h" 158c2ecf20Sopenharmony_ci#include "aic94xx_reg.h" 168c2ecf20Sopenharmony_ci#include "aic94xx_sds.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* ---------- OCM stuff ---------- */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct asd_ocm_dir_ent { 218c2ecf20Sopenharmony_ci u8 type; 228c2ecf20Sopenharmony_ci u8 offs[3]; 238c2ecf20Sopenharmony_ci u8 _r1; 248c2ecf20Sopenharmony_ci u8 size[3]; 258c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct asd_ocm_dir { 288c2ecf20Sopenharmony_ci char sig[2]; 298c2ecf20Sopenharmony_ci u8 _r1[2]; 308c2ecf20Sopenharmony_ci u8 major; /* 0 */ 318c2ecf20Sopenharmony_ci u8 minor; /* 0 */ 328c2ecf20Sopenharmony_ci u8 _r2; 338c2ecf20Sopenharmony_ci u8 num_de; 348c2ecf20Sopenharmony_ci struct asd_ocm_dir_ent entry[15]; 358c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define OCM_DE_OCM_DIR 0x00 388c2ecf20Sopenharmony_ci#define OCM_DE_WIN_DRVR 0x01 398c2ecf20Sopenharmony_ci#define OCM_DE_BIOS_CHIM 0x02 408c2ecf20Sopenharmony_ci#define OCM_DE_RAID_ENGN 0x03 418c2ecf20Sopenharmony_ci#define OCM_DE_BIOS_INTL 0x04 428c2ecf20Sopenharmony_ci#define OCM_DE_BIOS_CHIM_OSM 0x05 438c2ecf20Sopenharmony_ci#define OCM_DE_BIOS_CHIM_DYNAMIC 0x06 448c2ecf20Sopenharmony_ci#define OCM_DE_ADDC2C_RES0 0x07 458c2ecf20Sopenharmony_ci#define OCM_DE_ADDC2C_RES1 0x08 468c2ecf20Sopenharmony_ci#define OCM_DE_ADDC2C_RES2 0x09 478c2ecf20Sopenharmony_ci#define OCM_DE_ADDC2C_RES3 0x0A 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define OCM_INIT_DIR_ENTRIES 5 508c2ecf20Sopenharmony_ci/*************************************************************************** 518c2ecf20Sopenharmony_ci* OCM directory default 528c2ecf20Sopenharmony_ci***************************************************************************/ 538c2ecf20Sopenharmony_cistatic struct asd_ocm_dir OCMDirInit = 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci .sig = {0x4D, 0x4F}, /* signature */ 568c2ecf20Sopenharmony_ci .num_de = OCM_INIT_DIR_ENTRIES, /* no. of directory entries */ 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/*************************************************************************** 608c2ecf20Sopenharmony_ci* OCM directory Entries default 618c2ecf20Sopenharmony_ci***************************************************************************/ 628c2ecf20Sopenharmony_cistatic struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] = 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .type = (OCM_DE_ADDC2C_RES0), /* Entry type */ 668c2ecf20Sopenharmony_ci .offs = {128}, /* Offset */ 678c2ecf20Sopenharmony_ci .size = {0, 4}, /* size */ 688c2ecf20Sopenharmony_ci }, 698c2ecf20Sopenharmony_ci { 708c2ecf20Sopenharmony_ci .type = (OCM_DE_ADDC2C_RES1), /* Entry type */ 718c2ecf20Sopenharmony_ci .offs = {128, 4}, /* Offset */ 728c2ecf20Sopenharmony_ci .size = {0, 4}, /* size */ 738c2ecf20Sopenharmony_ci }, 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .type = (OCM_DE_ADDC2C_RES2), /* Entry type */ 768c2ecf20Sopenharmony_ci .offs = {128, 8}, /* Offset */ 778c2ecf20Sopenharmony_ci .size = {0, 4}, /* size */ 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci { 808c2ecf20Sopenharmony_ci .type = (OCM_DE_ADDC2C_RES3), /* Entry type */ 818c2ecf20Sopenharmony_ci .offs = {128, 12}, /* Offset */ 828c2ecf20Sopenharmony_ci .size = {0, 4}, /* size */ 838c2ecf20Sopenharmony_ci }, 848c2ecf20Sopenharmony_ci { 858c2ecf20Sopenharmony_ci .type = (OCM_DE_WIN_DRVR), /* Entry type */ 868c2ecf20Sopenharmony_ci .offs = {128, 16}, /* Offset */ 878c2ecf20Sopenharmony_ci .size = {128, 235, 1}, /* size */ 888c2ecf20Sopenharmony_ci }, 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct asd_bios_chim_struct { 928c2ecf20Sopenharmony_ci char sig[4]; 938c2ecf20Sopenharmony_ci u8 major; /* 1 */ 948c2ecf20Sopenharmony_ci u8 minor; /* 0 */ 958c2ecf20Sopenharmony_ci u8 bios_major; 968c2ecf20Sopenharmony_ci u8 bios_minor; 978c2ecf20Sopenharmony_ci __le32 bios_build; 988c2ecf20Sopenharmony_ci u8 flags; 998c2ecf20Sopenharmony_ci u8 pci_slot; 1008c2ecf20Sopenharmony_ci __le16 ue_num; 1018c2ecf20Sopenharmony_ci __le16 ue_size; 1028c2ecf20Sopenharmony_ci u8 _r[14]; 1038c2ecf20Sopenharmony_ci /* The unit element array is right here. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/** 1088c2ecf20Sopenharmony_ci * asd_read_ocm_seg - read an on chip memory (OCM) segment 1098c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 1108c2ecf20Sopenharmony_ci * @buffer: where to write the read data 1118c2ecf20Sopenharmony_ci * @offs: offset into OCM where to read from 1128c2ecf20Sopenharmony_ci * @size: how many bytes to read 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Return the number of bytes not read. Return 0 on success. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic int asd_read_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, 1178c2ecf20Sopenharmony_ci u32 offs, int size) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u8 *p = buffer; 1208c2ecf20Sopenharmony_ci if (unlikely(asd_ha->iospace)) 1218c2ecf20Sopenharmony_ci asd_read_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); 1228c2ecf20Sopenharmony_ci else { 1238c2ecf20Sopenharmony_ci for ( ; size > 0; size--, offs++, p++) 1248c2ecf20Sopenharmony_ci *p = asd_read_ocm_byte(asd_ha, offs); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci return size; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int asd_read_ocm_dir(struct asd_ha_struct *asd_ha, 1308c2ecf20Sopenharmony_ci struct asd_ocm_dir *dir, u32 offs) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int err = asd_read_ocm_seg(asd_ha, dir, offs, sizeof(*dir)); 1338c2ecf20Sopenharmony_ci if (err) { 1348c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read ocm segment\n"); 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (dir->sig[0] != 'M' || dir->sig[1] != 'O') { 1398c2ecf20Sopenharmony_ci ASD_DPRINTK("no valid dir signature(%c%c) at start of OCM\n", 1408c2ecf20Sopenharmony_ci dir->sig[0], dir->sig[1]); 1418c2ecf20Sopenharmony_ci return -ENOENT; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (dir->major != 0) { 1448c2ecf20Sopenharmony_ci asd_printk("unsupported major version of ocm dir:0x%x\n", 1458c2ecf20Sopenharmony_ci dir->major); 1468c2ecf20Sopenharmony_ci return -ENOENT; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci dir->num_de &= 0xf; 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * asd_write_ocm_seg - write an on chip memory (OCM) segment 1548c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 1558c2ecf20Sopenharmony_ci * @buffer: where to read the write data 1568c2ecf20Sopenharmony_ci * @offs: offset into OCM to write to 1578c2ecf20Sopenharmony_ci * @size: how many bytes to write 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Return the number of bytes not written. Return 0 on success. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic void asd_write_ocm_seg(struct asd_ha_struct *asd_ha, void *buffer, 1628c2ecf20Sopenharmony_ci u32 offs, int size) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci u8 *p = buffer; 1658c2ecf20Sopenharmony_ci if (unlikely(asd_ha->iospace)) 1668c2ecf20Sopenharmony_ci asd_write_reg_string(asd_ha, buffer, offs+OCM_BASE_ADDR, size); 1678c2ecf20Sopenharmony_ci else { 1688c2ecf20Sopenharmony_ci for ( ; size > 0; size--, offs++, p++) 1698c2ecf20Sopenharmony_ci asd_write_ocm_byte(asd_ha, offs, *p); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci return; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define THREE_TO_NUM(X) ((X)[0] | ((X)[1] << 8) | ((X)[2] << 16)) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int asd_find_dir_entry(struct asd_ocm_dir *dir, u8 type, 1778c2ecf20Sopenharmony_ci u32 *offs, u32 *size) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int i; 1808c2ecf20Sopenharmony_ci struct asd_ocm_dir_ent *ent; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < dir->num_de; i++) { 1838c2ecf20Sopenharmony_ci if (dir->entry[i].type == type) 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci if (i >= dir->num_de) 1878c2ecf20Sopenharmony_ci return -ENOENT; 1888c2ecf20Sopenharmony_ci ent = &dir->entry[i]; 1898c2ecf20Sopenharmony_ci *offs = (u32) THREE_TO_NUM(ent->offs); 1908c2ecf20Sopenharmony_ci *size = (u32) THREE_TO_NUM(ent->size); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#define OCM_BIOS_CHIM_DE 2 1958c2ecf20Sopenharmony_ci#define BC_BIOS_PRESENT 1 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int asd_get_bios_chim(struct asd_ha_struct *asd_ha, 1988c2ecf20Sopenharmony_ci struct asd_ocm_dir *dir) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int err; 2018c2ecf20Sopenharmony_ci struct asd_bios_chim_struct *bc_struct; 2028c2ecf20Sopenharmony_ci u32 offs, size; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci err = asd_find_dir_entry(dir, OCM_BIOS_CHIM_DE, &offs, &size); 2058c2ecf20Sopenharmony_ci if (err) { 2068c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't find BIOS_CHIM dir ent\n"); 2078c2ecf20Sopenharmony_ci goto out; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci err = -ENOMEM; 2108c2ecf20Sopenharmony_ci bc_struct = kmalloc(sizeof(*bc_struct), GFP_KERNEL); 2118c2ecf20Sopenharmony_ci if (!bc_struct) { 2128c2ecf20Sopenharmony_ci asd_printk("no memory for bios_chim struct\n"); 2138c2ecf20Sopenharmony_ci goto out; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci err = asd_read_ocm_seg(asd_ha, (void *)bc_struct, offs, 2168c2ecf20Sopenharmony_ci sizeof(*bc_struct)); 2178c2ecf20Sopenharmony_ci if (err) { 2188c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read ocm segment\n"); 2198c2ecf20Sopenharmony_ci goto out2; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci if (strncmp(bc_struct->sig, "SOIB", 4) 2228c2ecf20Sopenharmony_ci && strncmp(bc_struct->sig, "IPSA", 4)) { 2238c2ecf20Sopenharmony_ci ASD_DPRINTK("BIOS_CHIM entry has no valid sig(%c%c%c%c)\n", 2248c2ecf20Sopenharmony_ci bc_struct->sig[0], bc_struct->sig[1], 2258c2ecf20Sopenharmony_ci bc_struct->sig[2], bc_struct->sig[3]); 2268c2ecf20Sopenharmony_ci err = -ENOENT; 2278c2ecf20Sopenharmony_ci goto out2; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci if (bc_struct->major != 1) { 2308c2ecf20Sopenharmony_ci asd_printk("BIOS_CHIM unsupported major version:0x%x\n", 2318c2ecf20Sopenharmony_ci bc_struct->major); 2328c2ecf20Sopenharmony_ci err = -ENOENT; 2338c2ecf20Sopenharmony_ci goto out2; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (bc_struct->flags & BC_BIOS_PRESENT) { 2368c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.present = 1; 2378c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.maj = bc_struct->bios_major; 2388c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.min = bc_struct->bios_minor; 2398c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.bld = le32_to_cpu(bc_struct->bios_build); 2408c2ecf20Sopenharmony_ci ASD_DPRINTK("BIOS present (%d,%d), %d\n", 2418c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.maj, 2428c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.min, 2438c2ecf20Sopenharmony_ci asd_ha->hw_prof.bios.bld); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.num = le16_to_cpu(bc_struct->ue_num); 2468c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.size= le16_to_cpu(bc_struct->ue_size); 2478c2ecf20Sopenharmony_ci ASD_DPRINTK("ue num:%d, ue size:%d\n", asd_ha->hw_prof.ue.num, 2488c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.size); 2498c2ecf20Sopenharmony_ci size = asd_ha->hw_prof.ue.num * asd_ha->hw_prof.ue.size; 2508c2ecf20Sopenharmony_ci if (size > 0) { 2518c2ecf20Sopenharmony_ci err = -ENOMEM; 2528c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.area = kmalloc(size, GFP_KERNEL); 2538c2ecf20Sopenharmony_ci if (!asd_ha->hw_prof.ue.area) 2548c2ecf20Sopenharmony_ci goto out2; 2558c2ecf20Sopenharmony_ci err = asd_read_ocm_seg(asd_ha, (void *)asd_ha->hw_prof.ue.area, 2568c2ecf20Sopenharmony_ci offs + sizeof(*bc_struct), size); 2578c2ecf20Sopenharmony_ci if (err) { 2588c2ecf20Sopenharmony_ci kfree(asd_ha->hw_prof.ue.area); 2598c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.area = NULL; 2608c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.num = 0; 2618c2ecf20Sopenharmony_ci asd_ha->hw_prof.ue.size = 0; 2628c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read ue entries(%d)\n", err); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ciout2: 2668c2ecf20Sopenharmony_ci kfree(bc_struct); 2678c2ecf20Sopenharmony_ciout: 2688c2ecf20Sopenharmony_ci return err; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void 2728c2ecf20Sopenharmony_ciasd_hwi_initialize_ocm_dir (struct asd_ha_struct *asd_ha) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci int i; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Zero OCM */ 2778c2ecf20Sopenharmony_ci for (i = 0; i < OCM_MAX_SIZE; i += 4) 2788c2ecf20Sopenharmony_ci asd_write_ocm_dword(asd_ha, i, 0); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Write Dir */ 2818c2ecf20Sopenharmony_ci asd_write_ocm_seg(asd_ha, &OCMDirInit, 0, 2828c2ecf20Sopenharmony_ci sizeof(struct asd_ocm_dir)); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Write Dir Entries */ 2858c2ecf20Sopenharmony_ci for (i = 0; i < OCM_INIT_DIR_ENTRIES; i++) 2868c2ecf20Sopenharmony_ci asd_write_ocm_seg(asd_ha, &OCMDirEntriesInit[i], 2878c2ecf20Sopenharmony_ci sizeof(struct asd_ocm_dir) + 2888c2ecf20Sopenharmony_ci (i * sizeof(struct asd_ocm_dir_ent)) 2898c2ecf20Sopenharmony_ci , sizeof(struct asd_ocm_dir_ent)); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int 2948c2ecf20Sopenharmony_ciasd_hwi_check_ocm_access (struct asd_ha_struct *asd_ha) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct pci_dev *pcidev = asd_ha->pcidev; 2978c2ecf20Sopenharmony_ci u32 reg; 2988c2ecf20Sopenharmony_ci int err = 0; 2998c2ecf20Sopenharmony_ci u32 v; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* check if OCM has been initialized by BIOS */ 3028c2ecf20Sopenharmony_ci reg = asd_read_reg_dword(asd_ha, EXSICNFGR); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!(reg & OCMINITIALIZED)) { 3058c2ecf20Sopenharmony_ci err = pci_read_config_dword(pcidev, PCIC_INTRPT_STAT, &v); 3068c2ecf20Sopenharmony_ci if (err) { 3078c2ecf20Sopenharmony_ci asd_printk("couldn't access PCIC_INTRPT_STAT of %s\n", 3088c2ecf20Sopenharmony_ci pci_name(pcidev)); 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci printk(KERN_INFO "OCM is not initialized by BIOS," 3138c2ecf20Sopenharmony_ci "reinitialize it and ignore it, current IntrptStatus" 3148c2ecf20Sopenharmony_ci "is 0x%x\n", v); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (v) 3178c2ecf20Sopenharmony_ci err = pci_write_config_dword(pcidev, 3188c2ecf20Sopenharmony_ci PCIC_INTRPT_STAT, v); 3198c2ecf20Sopenharmony_ci if (err) { 3208c2ecf20Sopenharmony_ci asd_printk("couldn't write PCIC_INTRPT_STAT of %s\n", 3218c2ecf20Sopenharmony_ci pci_name(pcidev)); 3228c2ecf20Sopenharmony_ci goto out; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci asd_hwi_initialize_ocm_dir(asd_ha); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ciout: 3298c2ecf20Sopenharmony_ci return err; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/** 3338c2ecf20Sopenharmony_ci * asd_read_ocm - read on chip memory (OCM) 3348c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ciint asd_read_ocm(struct asd_ha_struct *asd_ha) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int err; 3398c2ecf20Sopenharmony_ci struct asd_ocm_dir *dir; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (asd_hwi_check_ocm_access(asd_ha)) 3428c2ecf20Sopenharmony_ci return -1; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dir = kmalloc(sizeof(*dir), GFP_KERNEL); 3458c2ecf20Sopenharmony_ci if (!dir) { 3468c2ecf20Sopenharmony_ci asd_printk("no memory for ocm dir\n"); 3478c2ecf20Sopenharmony_ci return -ENOMEM; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci err = asd_read_ocm_dir(asd_ha, dir, 0); 3518c2ecf20Sopenharmony_ci if (err) 3528c2ecf20Sopenharmony_ci goto out; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci err = asd_get_bios_chim(asd_ha, dir); 3558c2ecf20Sopenharmony_ciout: 3568c2ecf20Sopenharmony_ci kfree(dir); 3578c2ecf20Sopenharmony_ci return err; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/* ---------- FLASH stuff ---------- */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define FLASH_RESET 0xF0 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci#define ASD_FLASH_SIZE 0x200000 3658c2ecf20Sopenharmony_ci#define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** " 3668c2ecf20Sopenharmony_ci#define FLASH_NEXT_ENTRY_OFFS 0x2000 3678c2ecf20Sopenharmony_ci#define FLASH_MAX_DIR_ENTRIES 32 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci#define FLASH_DE_TYPE_MASK 0x3FFFFFFF 3708c2ecf20Sopenharmony_ci#define FLASH_DE_MS 0x120 3718c2ecf20Sopenharmony_ci#define FLASH_DE_CTRL_A_USER 0xE0 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistruct asd_flash_de { 3748c2ecf20Sopenharmony_ci __le32 type; 3758c2ecf20Sopenharmony_ci __le32 offs; 3768c2ecf20Sopenharmony_ci __le32 pad_size; 3778c2ecf20Sopenharmony_ci __le32 image_size; 3788c2ecf20Sopenharmony_ci __le32 chksum; 3798c2ecf20Sopenharmony_ci u8 _r[12]; 3808c2ecf20Sopenharmony_ci u8 version[32]; 3818c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistruct asd_flash_dir { 3848c2ecf20Sopenharmony_ci u8 cookie[32]; 3858c2ecf20Sopenharmony_ci __le32 rev; /* 2 */ 3868c2ecf20Sopenharmony_ci __le32 chksum; 3878c2ecf20Sopenharmony_ci __le32 chksum_antidote; 3888c2ecf20Sopenharmony_ci __le32 bld; 3898c2ecf20Sopenharmony_ci u8 bld_id[32]; /* build id data */ 3908c2ecf20Sopenharmony_ci u8 ver_data[32]; /* date and time of build */ 3918c2ecf20Sopenharmony_ci __le32 ae_mask; 3928c2ecf20Sopenharmony_ci __le32 v_mask; 3938c2ecf20Sopenharmony_ci __le32 oc_mask; 3948c2ecf20Sopenharmony_ci u8 _r[20]; 3958c2ecf20Sopenharmony_ci struct asd_flash_de dir_entry[FLASH_MAX_DIR_ENTRIES]; 3968c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistruct asd_manuf_sec { 3998c2ecf20Sopenharmony_ci char sig[2]; /* 'S', 'M' */ 4008c2ecf20Sopenharmony_ci u16 offs_next; 4018c2ecf20Sopenharmony_ci u8 maj; /* 0 */ 4028c2ecf20Sopenharmony_ci u8 min; /* 0 */ 4038c2ecf20Sopenharmony_ci u16 chksum; 4048c2ecf20Sopenharmony_ci u16 size; 4058c2ecf20Sopenharmony_ci u8 _r[6]; 4068c2ecf20Sopenharmony_ci u8 sas_addr[SAS_ADDR_SIZE]; 4078c2ecf20Sopenharmony_ci u8 pcba_sn[ASD_PCBA_SN_SIZE]; 4088c2ecf20Sopenharmony_ci /* Here start the other segments */ 4098c2ecf20Sopenharmony_ci u8 linked_list[]; 4108c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistruct asd_manuf_phy_desc { 4138c2ecf20Sopenharmony_ci u8 state; /* low 4 bits */ 4148c2ecf20Sopenharmony_ci#define MS_PHY_STATE_ENABLED 0 4158c2ecf20Sopenharmony_ci#define MS_PHY_STATE_REPORTED 1 4168c2ecf20Sopenharmony_ci#define MS_PHY_STATE_HIDDEN 2 4178c2ecf20Sopenharmony_ci u8 phy_id; 4188c2ecf20Sopenharmony_ci u16 _r; 4198c2ecf20Sopenharmony_ci u8 phy_control_0; /* mode 5 reg 0x160 */ 4208c2ecf20Sopenharmony_ci u8 phy_control_1; /* mode 5 reg 0x161 */ 4218c2ecf20Sopenharmony_ci u8 phy_control_2; /* mode 5 reg 0x162 */ 4228c2ecf20Sopenharmony_ci u8 phy_control_3; /* mode 5 reg 0x163 */ 4238c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistruct asd_manuf_phy_param { 4268c2ecf20Sopenharmony_ci char sig[2]; /* 'P', 'M' */ 4278c2ecf20Sopenharmony_ci u16 next; 4288c2ecf20Sopenharmony_ci u8 maj; /* 0 */ 4298c2ecf20Sopenharmony_ci u8 min; /* 2 */ 4308c2ecf20Sopenharmony_ci u8 num_phy_desc; /* 8 */ 4318c2ecf20Sopenharmony_ci u8 phy_desc_size; /* 8 */ 4328c2ecf20Sopenharmony_ci u8 _r[3]; 4338c2ecf20Sopenharmony_ci u8 usage_model_id; 4348c2ecf20Sopenharmony_ci u32 _r2; 4358c2ecf20Sopenharmony_ci struct asd_manuf_phy_desc phy_desc[ASD_MAX_PHYS]; 4368c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci#if 0 4398c2ecf20Sopenharmony_cistatic const char *asd_sb_type[] = { 4408c2ecf20Sopenharmony_ci "unknown", 4418c2ecf20Sopenharmony_ci "SGPIO", 4428c2ecf20Sopenharmony_ci [2 ... 0x7F] = "unknown", 4438c2ecf20Sopenharmony_ci [0x80] = "ADPT_I2C", 4448c2ecf20Sopenharmony_ci [0x81 ... 0xFF] = "VENDOR_UNIQUExx" 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci#endif 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistruct asd_ms_sb_desc { 4498c2ecf20Sopenharmony_ci u8 type; 4508c2ecf20Sopenharmony_ci u8 node_desc_index; 4518c2ecf20Sopenharmony_ci u8 conn_desc_index; 4528c2ecf20Sopenharmony_ci u8 _recvd[]; 4538c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci#if 0 4568c2ecf20Sopenharmony_cistatic const char *asd_conn_type[] = { 4578c2ecf20Sopenharmony_ci [0 ... 7] = "unknown", 4588c2ecf20Sopenharmony_ci "SFF8470", 4598c2ecf20Sopenharmony_ci "SFF8482", 4608c2ecf20Sopenharmony_ci "SFF8484", 4618c2ecf20Sopenharmony_ci [0x80] = "PCIX_DAUGHTER0", 4628c2ecf20Sopenharmony_ci [0x81] = "SAS_DAUGHTER0", 4638c2ecf20Sopenharmony_ci [0x82 ... 0xFF] = "VENDOR_UNIQUExx" 4648c2ecf20Sopenharmony_ci}; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic const char *asd_conn_location[] = { 4678c2ecf20Sopenharmony_ci "unknown", 4688c2ecf20Sopenharmony_ci "internal", 4698c2ecf20Sopenharmony_ci "external", 4708c2ecf20Sopenharmony_ci "board_to_board", 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistruct asd_ms_conn_desc { 4758c2ecf20Sopenharmony_ci u8 type; 4768c2ecf20Sopenharmony_ci u8 location; 4778c2ecf20Sopenharmony_ci u8 num_sideband_desc; 4788c2ecf20Sopenharmony_ci u8 size_sideband_desc; 4798c2ecf20Sopenharmony_ci u32 _resvd; 4808c2ecf20Sopenharmony_ci u8 name[16]; 4818c2ecf20Sopenharmony_ci struct asd_ms_sb_desc sb_desc[]; 4828c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistruct asd_nd_phy_desc { 4858c2ecf20Sopenharmony_ci u8 vp_attch_type; 4868c2ecf20Sopenharmony_ci u8 attch_specific[]; 4878c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci#if 0 4908c2ecf20Sopenharmony_cistatic const char *asd_node_type[] = { 4918c2ecf20Sopenharmony_ci "IOP", 4928c2ecf20Sopenharmony_ci "IO_CONTROLLER", 4938c2ecf20Sopenharmony_ci "EXPANDER", 4948c2ecf20Sopenharmony_ci "PORT_MULTIPLIER", 4958c2ecf20Sopenharmony_ci "PORT_MULTIPLEXER", 4968c2ecf20Sopenharmony_ci "MULTI_DROP_I2C_BUS", 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci#endif 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistruct asd_ms_node_desc { 5018c2ecf20Sopenharmony_ci u8 type; 5028c2ecf20Sopenharmony_ci u8 num_phy_desc; 5038c2ecf20Sopenharmony_ci u8 size_phy_desc; 5048c2ecf20Sopenharmony_ci u8 _resvd; 5058c2ecf20Sopenharmony_ci u8 name[16]; 5068c2ecf20Sopenharmony_ci struct asd_nd_phy_desc phy_desc[]; 5078c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistruct asd_ms_conn_map { 5108c2ecf20Sopenharmony_ci char sig[2]; /* 'M', 'C' */ 5118c2ecf20Sopenharmony_ci __le16 next; 5128c2ecf20Sopenharmony_ci u8 maj; /* 0 */ 5138c2ecf20Sopenharmony_ci u8 min; /* 0 */ 5148c2ecf20Sopenharmony_ci __le16 cm_size; /* size of this struct */ 5158c2ecf20Sopenharmony_ci u8 num_conn; 5168c2ecf20Sopenharmony_ci u8 conn_size; 5178c2ecf20Sopenharmony_ci u8 num_nodes; 5188c2ecf20Sopenharmony_ci u8 usage_model_id; 5198c2ecf20Sopenharmony_ci u32 _resvd; 5208c2ecf20Sopenharmony_ci struct asd_ms_conn_desc conn_desc[0]; 5218c2ecf20Sopenharmony_ci struct asd_ms_node_desc node_desc[]; 5228c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistruct asd_ctrla_phy_entry { 5258c2ecf20Sopenharmony_ci u8 sas_addr[SAS_ADDR_SIZE]; 5268c2ecf20Sopenharmony_ci u8 sas_link_rates; /* max in hi bits, min in low bits */ 5278c2ecf20Sopenharmony_ci u8 flags; 5288c2ecf20Sopenharmony_ci u8 sata_link_rates; 5298c2ecf20Sopenharmony_ci u8 _r[5]; 5308c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistruct asd_ctrla_phy_settings { 5338c2ecf20Sopenharmony_ci u8 id0; /* P'h'y */ 5348c2ecf20Sopenharmony_ci u8 _r; 5358c2ecf20Sopenharmony_ci u16 next; 5368c2ecf20Sopenharmony_ci u8 num_phys; /* number of PHYs in the PCI function */ 5378c2ecf20Sopenharmony_ci u8 _r2[3]; 5388c2ecf20Sopenharmony_ci struct asd_ctrla_phy_entry phy_ent[ASD_MAX_PHYS]; 5398c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistruct asd_ll_el { 5428c2ecf20Sopenharmony_ci u8 id0; 5438c2ecf20Sopenharmony_ci u8 id1; 5448c2ecf20Sopenharmony_ci __le16 next; 5458c2ecf20Sopenharmony_ci u8 something_here[]; 5468c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic int asd_poll_flash(struct asd_ha_struct *asd_ha) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci int c; 5518c2ecf20Sopenharmony_ci u8 d; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for (c = 5000; c > 0; c--) { 5548c2ecf20Sopenharmony_ci d = asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); 5558c2ecf20Sopenharmony_ci d ^= asd_read_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar); 5568c2ecf20Sopenharmony_ci if (!d) 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci udelay(5); 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci return -ENOENT; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int asd_reset_flash(struct asd_ha_struct *asd_ha) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci int err; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci err = asd_poll_flash(asd_ha); 5688c2ecf20Sopenharmony_ci if (err) 5698c2ecf20Sopenharmony_ci return err; 5708c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, asd_ha->hw_prof.flash.bar, FLASH_RESET); 5718c2ecf20Sopenharmony_ci err = asd_poll_flash(asd_ha); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return err; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int asd_read_flash_seg(struct asd_ha_struct *asd_ha, 5778c2ecf20Sopenharmony_ci void *buffer, u32 offs, int size) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs, 5808c2ecf20Sopenharmony_ci size); 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/** 5858c2ecf20Sopenharmony_ci * asd_find_flash_dir - finds and reads the flash directory 5868c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 5878c2ecf20Sopenharmony_ci * @flash_dir: pointer to flash directory structure 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * If found, the flash directory segment will be copied to 5908c2ecf20Sopenharmony_ci * @flash_dir. Return 1 if found, 0 if not. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic int asd_find_flash_dir(struct asd_ha_struct *asd_ha, 5938c2ecf20Sopenharmony_ci struct asd_flash_dir *flash_dir) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci u32 v; 5968c2ecf20Sopenharmony_ci for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) { 5978c2ecf20Sopenharmony_ci asd_read_flash_seg(asd_ha, flash_dir, v, 5988c2ecf20Sopenharmony_ci sizeof(FLASH_DIR_COOKIE)-1); 5998c2ecf20Sopenharmony_ci if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE, 6008c2ecf20Sopenharmony_ci sizeof(FLASH_DIR_COOKIE)-1) == 0) { 6018c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.dir_offs = v; 6028c2ecf20Sopenharmony_ci asd_read_flash_seg(asd_ha, flash_dir, v, 6038c2ecf20Sopenharmony_ci sizeof(*flash_dir)); 6048c2ecf20Sopenharmony_ci return 1; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int asd_flash_getid(struct asd_ha_struct *asd_ha) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci int err = 0; 6138c2ecf20Sopenharmony_ci u32 reg; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci reg = asd_read_reg_dword(asd_ha, EXSICNFGR); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR, 6188c2ecf20Sopenharmony_ci &asd_ha->hw_prof.flash.bar)) { 6198c2ecf20Sopenharmony_ci asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n", 6208c2ecf20Sopenharmony_ci pci_name(asd_ha->pcidev)); 6218c2ecf20Sopenharmony_ci return -ENOENT; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.present = 1; 6248c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.wide = reg & FLASHW ? 1 : 0; 6258c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 6268c2ecf20Sopenharmony_ci if (err) { 6278c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash(%d)\n", err); 6288c2ecf20Sopenharmony_ci return err; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic u16 asd_calc_flash_chksum(u16 *p, int size) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci u16 chksum = 0; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci while (size-- > 0) 6388c2ecf20Sopenharmony_ci chksum += *p++; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return chksum; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int asd_find_flash_de(struct asd_flash_dir *flash_dir, u32 entry_type, 6458c2ecf20Sopenharmony_ci u32 *offs, u32 *size) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int i; 6488c2ecf20Sopenharmony_ci struct asd_flash_de *de; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci for (i = 0; i < FLASH_MAX_DIR_ENTRIES; i++) { 6518c2ecf20Sopenharmony_ci u32 type = le32_to_cpu(flash_dir->dir_entry[i].type); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci type &= FLASH_DE_TYPE_MASK; 6548c2ecf20Sopenharmony_ci if (type == entry_type) 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci if (i >= FLASH_MAX_DIR_ENTRIES) 6588c2ecf20Sopenharmony_ci return -ENOENT; 6598c2ecf20Sopenharmony_ci de = &flash_dir->dir_entry[i]; 6608c2ecf20Sopenharmony_ci *offs = le32_to_cpu(de->offs); 6618c2ecf20Sopenharmony_ci *size = le32_to_cpu(de->pad_size); 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int asd_validate_ms(struct asd_manuf_sec *ms) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci if (ms->sig[0] != 'S' || ms->sig[1] != 'M') { 6688c2ecf20Sopenharmony_ci ASD_DPRINTK("manuf sec: no valid sig(%c%c)\n", 6698c2ecf20Sopenharmony_ci ms->sig[0], ms->sig[1]); 6708c2ecf20Sopenharmony_ci return -ENOENT; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci if (ms->maj != 0) { 6738c2ecf20Sopenharmony_ci asd_printk("unsupported manuf. sector. major version:%x\n", 6748c2ecf20Sopenharmony_ci ms->maj); 6758c2ecf20Sopenharmony_ci return -ENOENT; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci ms->offs_next = le16_to_cpu((__force __le16) ms->offs_next); 6788c2ecf20Sopenharmony_ci ms->chksum = le16_to_cpu((__force __le16) ms->chksum); 6798c2ecf20Sopenharmony_ci ms->size = le16_to_cpu((__force __le16) ms->size); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (asd_calc_flash_chksum((u16 *)ms, ms->size/2)) { 6828c2ecf20Sopenharmony_ci asd_printk("failed manuf sector checksum\n"); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int asd_ms_get_sas_addr(struct asd_ha_struct *asd_ha, 6898c2ecf20Sopenharmony_ci struct asd_manuf_sec *ms) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci memcpy(asd_ha->hw_prof.sas_addr, ms->sas_addr, SAS_ADDR_SIZE); 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int asd_ms_get_pcba_sn(struct asd_ha_struct *asd_ha, 6968c2ecf20Sopenharmony_ci struct asd_manuf_sec *ms) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci memcpy(asd_ha->hw_prof.pcba_sn, ms->pcba_sn, ASD_PCBA_SN_SIZE); 6998c2ecf20Sopenharmony_ci asd_ha->hw_prof.pcba_sn[ASD_PCBA_SN_SIZE] = '\0'; 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/** 7048c2ecf20Sopenharmony_ci * asd_find_ll_by_id - find a linked list entry by its id 7058c2ecf20Sopenharmony_ci * @start: void pointer to the first element in the linked list 7068c2ecf20Sopenharmony_ci * @id0: the first byte of the id (offs 0) 7078c2ecf20Sopenharmony_ci * @id1: the second byte of the id (offs 1) 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * @start has to be the _base_ element start, since the 7108c2ecf20Sopenharmony_ci * linked list entries's offset is from this pointer. 7118c2ecf20Sopenharmony_ci * Some linked list entries use only the first id, in which case 7128c2ecf20Sopenharmony_ci * you can pass 0xFF for the second. 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_cistatic void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct asd_ll_el *el = start; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci do { 7198c2ecf20Sopenharmony_ci switch (id1) { 7208c2ecf20Sopenharmony_ci default: 7218c2ecf20Sopenharmony_ci if (el->id1 == id1) 7228c2ecf20Sopenharmony_ci case 0xFF: 7238c2ecf20Sopenharmony_ci if (el->id0 == id0) 7248c2ecf20Sopenharmony_ci return el; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci el = start + le16_to_cpu(el->next); 7278c2ecf20Sopenharmony_ci } while (el != start); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return NULL; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/** 7338c2ecf20Sopenharmony_ci * asd_ms_get_phy_params - get phy parameters from the manufacturing sector 7348c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 7358c2ecf20Sopenharmony_ci * @manuf_sec: pointer to the manufacturing sector 7368c2ecf20Sopenharmony_ci * 7378c2ecf20Sopenharmony_ci * The manufacturing sector contans also the linked list of sub-segments, 7388c2ecf20Sopenharmony_ci * since when it was read, its size was taken from the flash directory, 7398c2ecf20Sopenharmony_ci * not from the structure size. 7408c2ecf20Sopenharmony_ci * 7418c2ecf20Sopenharmony_ci * HIDDEN phys do not count in the total count. REPORTED phys cannot 7428c2ecf20Sopenharmony_ci * be enabled but are reported and counted towards the total. 7438c2ecf20Sopenharmony_ci * ENABLED phys are enabled by default and count towards the total. 7448c2ecf20Sopenharmony_ci * The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys 7458c2ecf20Sopenharmony_ci * merely specifies the number of phys the host adapter decided to 7468c2ecf20Sopenharmony_ci * report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN, 7478c2ecf20Sopenharmony_ci * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED. 7488c2ecf20Sopenharmony_ci * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2 7498c2ecf20Sopenharmony_ci * are actually enabled (enabled by default, max number of phys 7508c2ecf20Sopenharmony_ci * enableable in this case). 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistatic int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha, 7538c2ecf20Sopenharmony_ci struct asd_manuf_sec *manuf_sec) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int i; 7568c2ecf20Sopenharmony_ci int en_phys = 0; 7578c2ecf20Sopenharmony_ci int rep_phys = 0; 7588c2ecf20Sopenharmony_ci struct asd_manuf_phy_param *phy_param; 7598c2ecf20Sopenharmony_ci struct asd_manuf_phy_param dflt_phy_param; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci phy_param = asd_find_ll_by_id(manuf_sec, 'P', 'M'); 7628c2ecf20Sopenharmony_ci if (!phy_param) { 7638c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: no phy parameters found\n"); 7648c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: Creating default phy parameters\n"); 7658c2ecf20Sopenharmony_ci dflt_phy_param.sig[0] = 'P'; 7668c2ecf20Sopenharmony_ci dflt_phy_param.sig[1] = 'M'; 7678c2ecf20Sopenharmony_ci dflt_phy_param.maj = 0; 7688c2ecf20Sopenharmony_ci dflt_phy_param.min = 2; 7698c2ecf20Sopenharmony_ci dflt_phy_param.num_phy_desc = 8; 7708c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc_size = sizeof(struct asd_manuf_phy_desc); 7718c2ecf20Sopenharmony_ci for (i =0; i < ASD_MAX_PHYS; i++) { 7728c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].state = 0; 7738c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].phy_id = i; 7748c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].phy_control_0 = 0xf6; 7758c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].phy_control_1 = 0x10; 7768c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].phy_control_2 = 0x43; 7778c2ecf20Sopenharmony_ci dflt_phy_param.phy_desc[i].phy_control_3 = 0xeb; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci phy_param = &dflt_phy_param; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (phy_param->maj != 0) { 7858c2ecf20Sopenharmony_ci asd_printk("unsupported manuf. phy param major version:0x%x\n", 7868c2ecf20Sopenharmony_ci phy_param->maj); 7878c2ecf20Sopenharmony_ci return -ENOENT; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: num_phy_desc: %d\n", phy_param->num_phy_desc); 7918c2ecf20Sopenharmony_ci asd_ha->hw_prof.enabled_phys = 0; 7928c2ecf20Sopenharmony_ci for (i = 0; i < phy_param->num_phy_desc; i++) { 7938c2ecf20Sopenharmony_ci struct asd_manuf_phy_desc *pd = &phy_param->phy_desc[i]; 7948c2ecf20Sopenharmony_ci switch (pd->state & 0xF) { 7958c2ecf20Sopenharmony_ci case MS_PHY_STATE_HIDDEN: 7968c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: phy%d: HIDDEN\n", i); 7978c2ecf20Sopenharmony_ci continue; 7988c2ecf20Sopenharmony_ci case MS_PHY_STATE_REPORTED: 7998c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: phy%d: REPORTED\n", i); 8008c2ecf20Sopenharmony_ci asd_ha->hw_prof.enabled_phys &= ~(1 << i); 8018c2ecf20Sopenharmony_ci rep_phys++; 8028c2ecf20Sopenharmony_ci continue; 8038c2ecf20Sopenharmony_ci case MS_PHY_STATE_ENABLED: 8048c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: phy%d: ENABLED\n", i); 8058c2ecf20Sopenharmony_ci asd_ha->hw_prof.enabled_phys |= (1 << i); 8068c2ecf20Sopenharmony_ci en_phys++; 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].phy_control_0 = pd->phy_control_0; 8108c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].phy_control_1 = pd->phy_control_1; 8118c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].phy_control_2 = pd->phy_control_2; 8128c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].phy_control_3 = pd->phy_control_3; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_phys = rep_phys + en_phys; 8158c2ecf20Sopenharmony_ci asd_ha->hw_prof.num_phys = en_phys; 8168c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: max_phys:0x%x, num_phys:0x%x\n", 8178c2ecf20Sopenharmony_ci asd_ha->hw_prof.max_phys, asd_ha->hw_prof.num_phys); 8188c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: enabled_phys:0x%x\n", asd_ha->hw_prof.enabled_phys); 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int asd_ms_get_connector_map(struct asd_ha_struct *asd_ha, 8238c2ecf20Sopenharmony_ci struct asd_manuf_sec *manuf_sec) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct asd_ms_conn_map *cm; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci cm = asd_find_ll_by_id(manuf_sec, 'M', 'C'); 8288c2ecf20Sopenharmony_ci if (!cm) { 8298c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: no connector map found\n"); 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (cm->maj != 0) { 8348c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: unsupported: connector map major version 0x%x" 8358c2ecf20Sopenharmony_ci "\n", cm->maj); 8368c2ecf20Sopenharmony_ci return -ENOENT; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* XXX */ 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci return 0; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * asd_process_ms - find and extract information from the manufacturing sector 8478c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 8488c2ecf20Sopenharmony_ci * @flash_dir: pointer to the flash directory 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_cistatic int asd_process_ms(struct asd_ha_struct *asd_ha, 8518c2ecf20Sopenharmony_ci struct asd_flash_dir *flash_dir) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci int err; 8548c2ecf20Sopenharmony_ci struct asd_manuf_sec *manuf_sec; 8558c2ecf20Sopenharmony_ci u32 offs, size; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci err = asd_find_flash_de(flash_dir, FLASH_DE_MS, &offs, &size); 8588c2ecf20Sopenharmony_ci if (err) { 8598c2ecf20Sopenharmony_ci ASD_DPRINTK("Couldn't find the manuf. sector\n"); 8608c2ecf20Sopenharmony_ci goto out; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (size == 0) 8648c2ecf20Sopenharmony_ci goto out; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci err = -ENOMEM; 8678c2ecf20Sopenharmony_ci manuf_sec = kmalloc(size, GFP_KERNEL); 8688c2ecf20Sopenharmony_ci if (!manuf_sec) { 8698c2ecf20Sopenharmony_ci ASD_DPRINTK("no mem for manuf sector\n"); 8708c2ecf20Sopenharmony_ci goto out; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci err = asd_read_flash_seg(asd_ha, (void *)manuf_sec, offs, size); 8748c2ecf20Sopenharmony_ci if (err) { 8758c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read manuf sector at 0x%x, size 0x%x\n", 8768c2ecf20Sopenharmony_ci offs, size); 8778c2ecf20Sopenharmony_ci goto out2; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci err = asd_validate_ms(manuf_sec); 8818c2ecf20Sopenharmony_ci if (err) { 8828c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't validate manuf sector\n"); 8838c2ecf20Sopenharmony_ci goto out2; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci err = asd_ms_get_sas_addr(asd_ha, manuf_sec); 8878c2ecf20Sopenharmony_ci if (err) { 8888c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read the SAS_ADDR\n"); 8898c2ecf20Sopenharmony_ci goto out2; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci ASD_DPRINTK("manuf sect SAS_ADDR %llx\n", 8928c2ecf20Sopenharmony_ci SAS_ADDR(asd_ha->hw_prof.sas_addr)); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci err = asd_ms_get_pcba_sn(asd_ha, manuf_sec); 8958c2ecf20Sopenharmony_ci if (err) { 8968c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read the PCBA SN\n"); 8978c2ecf20Sopenharmony_ci goto out2; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci ASD_DPRINTK("manuf sect PCBA SN %s\n", asd_ha->hw_prof.pcba_sn); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci err = asd_ms_get_phy_params(asd_ha, manuf_sec); 9028c2ecf20Sopenharmony_ci if (err) { 9038c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: couldn't get phy parameters\n"); 9048c2ecf20Sopenharmony_ci goto out2; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci err = asd_ms_get_connector_map(asd_ha, manuf_sec); 9088c2ecf20Sopenharmony_ci if (err) { 9098c2ecf20Sopenharmony_ci ASD_DPRINTK("ms: couldn't get connector map\n"); 9108c2ecf20Sopenharmony_ci goto out2; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ciout2: 9148c2ecf20Sopenharmony_ci kfree(manuf_sec); 9158c2ecf20Sopenharmony_ciout: 9168c2ecf20Sopenharmony_ci return err; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int asd_process_ctrla_phy_settings(struct asd_ha_struct *asd_ha, 9208c2ecf20Sopenharmony_ci struct asd_ctrla_phy_settings *ps) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci int i; 9238c2ecf20Sopenharmony_ci for (i = 0; i < ps->num_phys; i++) { 9248c2ecf20Sopenharmony_ci struct asd_ctrla_phy_entry *pe = &ps->phy_ent[i]; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (!PHY_ENABLED(asd_ha, i)) 9278c2ecf20Sopenharmony_ci continue; 9288c2ecf20Sopenharmony_ci if (*(u64 *)pe->sas_addr == 0) { 9298c2ecf20Sopenharmony_ci asd_ha->hw_prof.enabled_phys &= ~(1 << i); 9308c2ecf20Sopenharmony_ci continue; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci /* This is the SAS address which should be sent in IDENTIFY. */ 9338c2ecf20Sopenharmony_ci memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr, pe->sas_addr, 9348c2ecf20Sopenharmony_ci SAS_ADDR_SIZE); 9358c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].max_sas_lrate = 9368c2ecf20Sopenharmony_ci (pe->sas_link_rates & 0xF0) >> 4; 9378c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].min_sas_lrate = 9388c2ecf20Sopenharmony_ci (pe->sas_link_rates & 0x0F); 9398c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].max_sata_lrate = 9408c2ecf20Sopenharmony_ci (pe->sata_link_rates & 0xF0) >> 4; 9418c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].min_sata_lrate = 9428c2ecf20Sopenharmony_ci (pe->sata_link_rates & 0x0F); 9438c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].flags = pe->flags; 9448c2ecf20Sopenharmony_ci ASD_DPRINTK("ctrla: phy%d: sas_addr: %llx, sas rate:0x%x-0x%x," 9458c2ecf20Sopenharmony_ci " sata rate:0x%x-0x%x, flags:0x%x\n", 9468c2ecf20Sopenharmony_ci i, 9478c2ecf20Sopenharmony_ci SAS_ADDR(asd_ha->hw_prof.phy_desc[i].sas_addr), 9488c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].max_sas_lrate, 9498c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].min_sas_lrate, 9508c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].max_sata_lrate, 9518c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].min_sata_lrate, 9528c2ecf20Sopenharmony_ci asd_ha->hw_prof.phy_desc[i].flags); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci/** 9598c2ecf20Sopenharmony_ci * asd_process_ctrl_a_user - process CTRL-A user settings 9608c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 9618c2ecf20Sopenharmony_ci * @flash_dir: pointer to the flash directory 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_cistatic int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, 9648c2ecf20Sopenharmony_ci struct asd_flash_dir *flash_dir) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci int err, i; 9678c2ecf20Sopenharmony_ci u32 offs, size; 9688c2ecf20Sopenharmony_ci struct asd_ll_el *el = NULL; 9698c2ecf20Sopenharmony_ci struct asd_ctrla_phy_settings *ps; 9708c2ecf20Sopenharmony_ci struct asd_ctrla_phy_settings dflt_ps; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci err = asd_find_flash_de(flash_dir, FLASH_DE_CTRL_A_USER, &offs, &size); 9738c2ecf20Sopenharmony_ci if (err) { 9748c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't find CTRL-A user settings section\n"); 9758c2ecf20Sopenharmony_ci ASD_DPRINTK("Creating default CTRL-A user settings section\n"); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci dflt_ps.id0 = 'h'; 9788c2ecf20Sopenharmony_ci dflt_ps.num_phys = 8; 9798c2ecf20Sopenharmony_ci for (i =0; i < ASD_MAX_PHYS; i++) { 9808c2ecf20Sopenharmony_ci memcpy(dflt_ps.phy_ent[i].sas_addr, 9818c2ecf20Sopenharmony_ci asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE); 9828c2ecf20Sopenharmony_ci dflt_ps.phy_ent[i].sas_link_rates = 0x98; 9838c2ecf20Sopenharmony_ci dflt_ps.phy_ent[i].flags = 0x0; 9848c2ecf20Sopenharmony_ci dflt_ps.phy_ent[i].sata_link_rates = 0x0; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci size = sizeof(struct asd_ctrla_phy_settings); 9888c2ecf20Sopenharmony_ci ps = &dflt_ps; 9898c2ecf20Sopenharmony_ci goto out_process; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (size == 0) 9938c2ecf20Sopenharmony_ci goto out; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci err = -ENOMEM; 9968c2ecf20Sopenharmony_ci el = kmalloc(size, GFP_KERNEL); 9978c2ecf20Sopenharmony_ci if (!el) { 9988c2ecf20Sopenharmony_ci ASD_DPRINTK("no mem for ctrla user settings section\n"); 9998c2ecf20Sopenharmony_ci goto out; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci err = asd_read_flash_seg(asd_ha, (void *)el, offs, size); 10038c2ecf20Sopenharmony_ci if (err) { 10048c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't read ctrla phy settings section\n"); 10058c2ecf20Sopenharmony_ci goto out2; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci err = -ENOENT; 10098c2ecf20Sopenharmony_ci ps = asd_find_ll_by_id(el, 'h', 0xFF); 10108c2ecf20Sopenharmony_ci if (!ps) { 10118c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); 10128c2ecf20Sopenharmony_ci goto out2; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ciout_process: 10158c2ecf20Sopenharmony_ci err = asd_process_ctrla_phy_settings(asd_ha, ps); 10168c2ecf20Sopenharmony_ci if (err) { 10178c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't process ctrla phy settings\n"); 10188c2ecf20Sopenharmony_ci goto out2; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ciout2: 10218c2ecf20Sopenharmony_ci kfree(el); 10228c2ecf20Sopenharmony_ciout: 10238c2ecf20Sopenharmony_ci return err; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci/** 10278c2ecf20Sopenharmony_ci * asd_read_flash - read flash memory 10288c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ciint asd_read_flash(struct asd_ha_struct *asd_ha) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci int err; 10338c2ecf20Sopenharmony_ci struct asd_flash_dir *flash_dir; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci err = asd_flash_getid(asd_ha); 10368c2ecf20Sopenharmony_ci if (err) 10378c2ecf20Sopenharmony_ci return err; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci flash_dir = kmalloc(sizeof(*flash_dir), GFP_KERNEL); 10408c2ecf20Sopenharmony_ci if (!flash_dir) 10418c2ecf20Sopenharmony_ci return -ENOMEM; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci err = -ENOENT; 10448c2ecf20Sopenharmony_ci if (!asd_find_flash_dir(asd_ha, flash_dir)) { 10458c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't find flash directory\n"); 10468c2ecf20Sopenharmony_ci goto out; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (le32_to_cpu(flash_dir->rev) != 2) { 10508c2ecf20Sopenharmony_ci asd_printk("unsupported flash dir version:0x%x\n", 10518c2ecf20Sopenharmony_ci le32_to_cpu(flash_dir->rev)); 10528c2ecf20Sopenharmony_ci goto out; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci err = asd_process_ms(asd_ha, flash_dir); 10568c2ecf20Sopenharmony_ci if (err) { 10578c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't process manuf sector settings\n"); 10588c2ecf20Sopenharmony_ci goto out; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci err = asd_process_ctrl_a_user(asd_ha, flash_dir); 10628c2ecf20Sopenharmony_ci if (err) { 10638c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't process CTRL-A user settings\n"); 10648c2ecf20Sopenharmony_ci goto out; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ciout: 10688c2ecf20Sopenharmony_ci kfree(flash_dir); 10698c2ecf20Sopenharmony_ci return err; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci/** 10738c2ecf20Sopenharmony_ci * asd_verify_flash_seg - verify data with flash memory 10748c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 10758c2ecf20Sopenharmony_ci * @src: pointer to the source data to be verified 10768c2ecf20Sopenharmony_ci * @dest_offset: offset from flash memory 10778c2ecf20Sopenharmony_ci * @bytes_to_verify: total bytes to verify 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_ciint asd_verify_flash_seg(struct asd_ha_struct *asd_ha, 10808c2ecf20Sopenharmony_ci const void *src, u32 dest_offset, u32 bytes_to_verify) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci const u8 *src_buf; 10838c2ecf20Sopenharmony_ci u8 flash_char; 10848c2ecf20Sopenharmony_ci int err; 10858c2ecf20Sopenharmony_ci u32 nv_offset, reg, i; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci reg = asd_ha->hw_prof.flash.bar; 10888c2ecf20Sopenharmony_ci src_buf = NULL; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci err = FLASH_OK; 10918c2ecf20Sopenharmony_ci nv_offset = dest_offset; 10928c2ecf20Sopenharmony_ci src_buf = (const u8 *)src; 10938c2ecf20Sopenharmony_ci for (i = 0; i < bytes_to_verify; i++) { 10948c2ecf20Sopenharmony_ci flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i); 10958c2ecf20Sopenharmony_ci if (flash_char != src_buf[i]) { 10968c2ecf20Sopenharmony_ci err = FAIL_VERIFY; 10978c2ecf20Sopenharmony_ci break; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci return err; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci/** 11048c2ecf20Sopenharmony_ci * asd_write_flash_seg - write data into flash memory 11058c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 11068c2ecf20Sopenharmony_ci * @src: pointer to the source data to be written 11078c2ecf20Sopenharmony_ci * @dest_offset: offset from flash memory 11088c2ecf20Sopenharmony_ci * @bytes_to_write: total bytes to write 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ciint asd_write_flash_seg(struct asd_ha_struct *asd_ha, 11118c2ecf20Sopenharmony_ci const void *src, u32 dest_offset, u32 bytes_to_write) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci const u8 *src_buf; 11148c2ecf20Sopenharmony_ci u32 nv_offset, reg, i; 11158c2ecf20Sopenharmony_ci int err; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci reg = asd_ha->hw_prof.flash.bar; 11188c2ecf20Sopenharmony_ci src_buf = NULL; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci err = asd_check_flash_type(asd_ha); 11218c2ecf20Sopenharmony_ci if (err) { 11228c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err); 11238c2ecf20Sopenharmony_ci return err; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci nv_offset = dest_offset; 11278c2ecf20Sopenharmony_ci err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write); 11288c2ecf20Sopenharmony_ci if (err) { 11298c2ecf20Sopenharmony_ci ASD_DPRINTK("Erase failed at offset:0x%x\n", 11308c2ecf20Sopenharmony_ci nv_offset); 11318c2ecf20Sopenharmony_ci return err; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 11358c2ecf20Sopenharmony_ci if (err) { 11368c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 11378c2ecf20Sopenharmony_ci return err; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci src_buf = (const u8 *)src; 11418c2ecf20Sopenharmony_ci for (i = 0; i < bytes_to_write; i++) { 11428c2ecf20Sopenharmony_ci /* Setup program command sequence */ 11438c2ecf20Sopenharmony_ci switch (asd_ha->hw_prof.flash.method) { 11448c2ecf20Sopenharmony_ci case FLASH_METHOD_A: 11458c2ecf20Sopenharmony_ci { 11468c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11478c2ecf20Sopenharmony_ci (reg + 0xAAA), 0xAA); 11488c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11498c2ecf20Sopenharmony_ci (reg + 0x555), 0x55); 11508c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11518c2ecf20Sopenharmony_ci (reg + 0xAAA), 0xA0); 11528c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11538c2ecf20Sopenharmony_ci (reg + nv_offset + i), 11548c2ecf20Sopenharmony_ci (*(src_buf + i))); 11558c2ecf20Sopenharmony_ci break; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci case FLASH_METHOD_B: 11588c2ecf20Sopenharmony_ci { 11598c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11608c2ecf20Sopenharmony_ci (reg + 0x555), 0xAA); 11618c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11628c2ecf20Sopenharmony_ci (reg + 0x2AA), 0x55); 11638c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11648c2ecf20Sopenharmony_ci (reg + 0x555), 0xA0); 11658c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, 11668c2ecf20Sopenharmony_ci (reg + nv_offset + i), 11678c2ecf20Sopenharmony_ci (*(src_buf + i))); 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci default: 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci if (asd_chk_write_status(asd_ha, 11748c2ecf20Sopenharmony_ci (nv_offset + i), 0) != 0) { 11758c2ecf20Sopenharmony_ci ASD_DPRINTK("aicx: Write failed at offset:0x%x\n", 11768c2ecf20Sopenharmony_ci reg + nv_offset + i); 11778c2ecf20Sopenharmony_ci return FAIL_WRITE_FLASH; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 11828c2ecf20Sopenharmony_ci if (err) { 11838c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 11848c2ecf20Sopenharmony_ci return err; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ciint asd_chk_write_status(struct asd_ha_struct *asd_ha, 11908c2ecf20Sopenharmony_ci u32 sector_addr, u8 erase_flag) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci u32 reg; 11938c2ecf20Sopenharmony_ci u32 loop_cnt; 11948c2ecf20Sopenharmony_ci u8 nv_data1, nv_data2; 11958c2ecf20Sopenharmony_ci u8 toggle_bit1; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * Read from DQ2 requires sector address 11998c2ecf20Sopenharmony_ci * while it's dont care for DQ6 12008c2ecf20Sopenharmony_ci */ 12018c2ecf20Sopenharmony_ci reg = asd_ha->hw_prof.flash.bar; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) { 12048c2ecf20Sopenharmony_ci nv_data1 = asd_read_reg_byte(asd_ha, reg); 12058c2ecf20Sopenharmony_ci nv_data2 = asd_read_reg_byte(asd_ha, reg); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) 12088c2ecf20Sopenharmony_ci ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (toggle_bit1 == 0) { 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci } else { 12138c2ecf20Sopenharmony_ci if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) { 12148c2ecf20Sopenharmony_ci nv_data1 = asd_read_reg_byte(asd_ha, 12158c2ecf20Sopenharmony_ci reg); 12168c2ecf20Sopenharmony_ci nv_data2 = asd_read_reg_byte(asd_ha, 12178c2ecf20Sopenharmony_ci reg); 12188c2ecf20Sopenharmony_ci toggle_bit1 = 12198c2ecf20Sopenharmony_ci ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) 12208c2ecf20Sopenharmony_ci ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (toggle_bit1 == 0) 12238c2ecf20Sopenharmony_ci return 0; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * ERASE is a sector-by-sector operation and requires 12298c2ecf20Sopenharmony_ci * more time to finish while WRITE is byte-byte-byte 12308c2ecf20Sopenharmony_ci * operation and takes lesser time to finish. 12318c2ecf20Sopenharmony_ci * 12328c2ecf20Sopenharmony_ci * For some strange reason a reduced ERASE delay gives different 12338c2ecf20Sopenharmony_ci * behaviour across different spirit boards. Hence we set 12348c2ecf20Sopenharmony_ci * a optimum balance of 50mus for ERASE which works well 12358c2ecf20Sopenharmony_ci * across all boards. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci if (erase_flag) { 12388c2ecf20Sopenharmony_ci udelay(FLASH_STATUS_ERASE_DELAY_COUNT); 12398c2ecf20Sopenharmony_ci } else { 12408c2ecf20Sopenharmony_ci udelay(FLASH_STATUS_WRITE_DELAY_COUNT); 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci return -1; 12448c2ecf20Sopenharmony_ci} 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci/** 12478c2ecf20Sopenharmony_ci * asd_hwi_erase_nv_sector - Erase the flash memory sectors. 12488c2ecf20Sopenharmony_ci * @asd_ha: pointer to the host adapter structure 12498c2ecf20Sopenharmony_ci * @flash_addr: pointer to offset from flash memory 12508c2ecf20Sopenharmony_ci * @size: total bytes to erase. 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_ciint asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci u32 reg; 12558c2ecf20Sopenharmony_ci u32 sector_addr; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci reg = asd_ha->hw_prof.flash.bar; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* sector staring address */ 12608c2ecf20Sopenharmony_ci sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* 12638c2ecf20Sopenharmony_ci * Erasing an flash sector needs to be done in six consecutive 12648c2ecf20Sopenharmony_ci * write cyles. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci while (sector_addr < flash_addr+size) { 12678c2ecf20Sopenharmony_ci switch (asd_ha->hw_prof.flash.method) { 12688c2ecf20Sopenharmony_ci case FLASH_METHOD_A: 12698c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); 12708c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); 12718c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80); 12728c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); 12738c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); 12748c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); 12758c2ecf20Sopenharmony_ci break; 12768c2ecf20Sopenharmony_ci case FLASH_METHOD_B: 12778c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); 12788c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); 12798c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80); 12808c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); 12818c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); 12828c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); 12838c2ecf20Sopenharmony_ci break; 12848c2ecf20Sopenharmony_ci default: 12858c2ecf20Sopenharmony_ci break; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0) 12898c2ecf20Sopenharmony_ci return FAIL_ERASE_FLASH; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci sector_addr += FLASH_SECTOR_SIZE; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci return 0; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ciint asd_check_flash_type(struct asd_ha_struct *asd_ha) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci u8 manuf_id; 13008c2ecf20Sopenharmony_ci u8 dev_id; 13018c2ecf20Sopenharmony_ci u8 sec_prot; 13028c2ecf20Sopenharmony_ci u32 inc; 13038c2ecf20Sopenharmony_ci u32 reg; 13048c2ecf20Sopenharmony_ci int err; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci /* get Flash memory base address */ 13078c2ecf20Sopenharmony_ci reg = asd_ha->hw_prof.flash.bar; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* Determine flash info */ 13108c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 13118c2ecf20Sopenharmony_ci if (err) { 13128c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 13138c2ecf20Sopenharmony_ci return err; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN; 13178c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN; 13188c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci /* Get flash info. This would most likely be AMD Am29LV family flash. 13218c2ecf20Sopenharmony_ci * First try the sequence for word mode. It is the same as for 13228c2ecf20Sopenharmony_ci * 008B (byte mode only), 160B (word mode) and 800D (word mode). 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_ci inc = asd_ha->hw_prof.flash.wide ? 2 : 1; 13258c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); 13268c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); 13278c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); 13288c2ecf20Sopenharmony_ci manuf_id = asd_read_reg_byte(asd_ha, reg); 13298c2ecf20Sopenharmony_ci dev_id = asd_read_reg_byte(asd_ha, reg + inc); 13308c2ecf20Sopenharmony_ci sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); 13318c2ecf20Sopenharmony_ci /* Get out of autoselect mode. */ 13328c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 13338c2ecf20Sopenharmony_ci if (err) { 13348c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 13358c2ecf20Sopenharmony_ci return err; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) " 13388c2ecf20Sopenharmony_ci "sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot); 13398c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 13408c2ecf20Sopenharmony_ci if (err != 0) 13418c2ecf20Sopenharmony_ci return err; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci switch (manuf_id) { 13448c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_AMD: 13458c2ecf20Sopenharmony_ci switch (sec_prot) { 13468c2ecf20Sopenharmony_ci case FLASH_DEV_ID_AM29LV800DT: 13478c2ecf20Sopenharmony_ci case FLASH_DEV_ID_AM29LV640MT: 13488c2ecf20Sopenharmony_ci case FLASH_DEV_ID_AM29F800B: 13498c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_A; 13508c2ecf20Sopenharmony_ci break; 13518c2ecf20Sopenharmony_ci default: 13528c2ecf20Sopenharmony_ci break; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci break; 13558c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_ST: 13568c2ecf20Sopenharmony_ci switch (sec_prot) { 13578c2ecf20Sopenharmony_ci case FLASH_DEV_ID_STM29W800DT: 13588c2ecf20Sopenharmony_ci case FLASH_DEV_ID_STM29LV640: 13598c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_A; 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci default: 13628c2ecf20Sopenharmony_ci break; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_FUJITSU: 13668c2ecf20Sopenharmony_ci switch (sec_prot) { 13678c2ecf20Sopenharmony_ci case FLASH_DEV_ID_MBM29LV800TE: 13688c2ecf20Sopenharmony_ci case FLASH_DEV_ID_MBM29DL800TA: 13698c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_A; 13708c2ecf20Sopenharmony_ci break; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_MACRONIX: 13748c2ecf20Sopenharmony_ci switch (sec_prot) { 13758c2ecf20Sopenharmony_ci case FLASH_DEV_ID_MX29LV800BT: 13768c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_A; 13778c2ecf20Sopenharmony_ci break; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci break; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) { 13838c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 13848c2ecf20Sopenharmony_ci if (err) { 13858c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 13868c2ecf20Sopenharmony_ci return err; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci /* Issue Unlock sequence for AM29LV008BT */ 13908c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); 13918c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); 13928c2ecf20Sopenharmony_ci asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90); 13938c2ecf20Sopenharmony_ci manuf_id = asd_read_reg_byte(asd_ha, reg); 13948c2ecf20Sopenharmony_ci dev_id = asd_read_reg_byte(asd_ha, reg + inc); 13958c2ecf20Sopenharmony_ci sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot" 13988c2ecf20Sopenharmony_ci "(0x%x)\n", manuf_id, dev_id, sec_prot); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci err = asd_reset_flash(asd_ha); 14018c2ecf20Sopenharmony_ci if (err != 0) { 14028c2ecf20Sopenharmony_ci ASD_DPRINTK("couldn't reset flash. err=%d\n", err); 14038c2ecf20Sopenharmony_ci return err; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci switch (manuf_id) { 14078c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_AMD: 14088c2ecf20Sopenharmony_ci switch (dev_id) { 14098c2ecf20Sopenharmony_ci case FLASH_DEV_ID_AM29LV008BT: 14108c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_B; 14118c2ecf20Sopenharmony_ci break; 14128c2ecf20Sopenharmony_ci default: 14138c2ecf20Sopenharmony_ci break; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci break; 14168c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_ST: 14178c2ecf20Sopenharmony_ci switch (dev_id) { 14188c2ecf20Sopenharmony_ci case FLASH_DEV_ID_STM29008: 14198c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_B; 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci default: 14228c2ecf20Sopenharmony_ci break; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_FUJITSU: 14268c2ecf20Sopenharmony_ci switch (dev_id) { 14278c2ecf20Sopenharmony_ci case FLASH_DEV_ID_MBM29LV008TA: 14288c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_B; 14298c2ecf20Sopenharmony_ci break; 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_INTEL: 14338c2ecf20Sopenharmony_ci switch (dev_id) { 14348c2ecf20Sopenharmony_ci case FLASH_DEV_ID_I28LV00TAT: 14358c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_B; 14368c2ecf20Sopenharmony_ci break; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci break; 14398c2ecf20Sopenharmony_ci case FLASH_MANUF_ID_MACRONIX: 14408c2ecf20Sopenharmony_ci switch (dev_id) { 14418c2ecf20Sopenharmony_ci case FLASH_DEV_ID_I28LV00TAT: 14428c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.method = FLASH_METHOD_B; 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci break; 14468c2ecf20Sopenharmony_ci default: 14478c2ecf20Sopenharmony_ci return FAIL_FIND_FLASH_ID; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) 14528c2ecf20Sopenharmony_ci return FAIL_FIND_FLASH_ID; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.manuf = manuf_id; 14558c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.dev_id = dev_id; 14568c2ecf20Sopenharmony_ci asd_ha->hw_prof.flash.sec_prot = sec_prot; 14578c2ecf20Sopenharmony_ci return 0; 14588c2ecf20Sopenharmony_ci} 1459