18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * nvme-lightnvm.c - LightNVM NVMe device 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 IT University of Copenhagen 68c2ecf20Sopenharmony_ci * Initial release: Matias Bjorling <mb@lightnvm.io> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "nvme.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/nvme.h> 128c2ecf20Sopenharmony_ci#include <linux/bitops.h> 138c2ecf20Sopenharmony_ci#include <linux/lightnvm.h> 148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/sched/sysctl.h> 168c2ecf20Sopenharmony_ci#include <uapi/linux/lightnvm.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cienum nvme_nvm_admin_opcode { 198c2ecf20Sopenharmony_ci nvme_nvm_admin_identity = 0xe2, 208c2ecf20Sopenharmony_ci nvme_nvm_admin_get_bb_tbl = 0xf2, 218c2ecf20Sopenharmony_ci nvme_nvm_admin_set_bb_tbl = 0xf1, 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum nvme_nvm_log_page { 258c2ecf20Sopenharmony_ci NVME_NVM_LOG_REPORT_CHUNK = 0xca, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct nvme_nvm_ph_rw { 298c2ecf20Sopenharmony_ci __u8 opcode; 308c2ecf20Sopenharmony_ci __u8 flags; 318c2ecf20Sopenharmony_ci __u16 command_id; 328c2ecf20Sopenharmony_ci __le32 nsid; 338c2ecf20Sopenharmony_ci __u64 rsvd2; 348c2ecf20Sopenharmony_ci __le64 metadata; 358c2ecf20Sopenharmony_ci __le64 prp1; 368c2ecf20Sopenharmony_ci __le64 prp2; 378c2ecf20Sopenharmony_ci __le64 spba; 388c2ecf20Sopenharmony_ci __le16 length; 398c2ecf20Sopenharmony_ci __le16 control; 408c2ecf20Sopenharmony_ci __le32 dsmgmt; 418c2ecf20Sopenharmony_ci __le64 resv; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct nvme_nvm_erase_blk { 458c2ecf20Sopenharmony_ci __u8 opcode; 468c2ecf20Sopenharmony_ci __u8 flags; 478c2ecf20Sopenharmony_ci __u16 command_id; 488c2ecf20Sopenharmony_ci __le32 nsid; 498c2ecf20Sopenharmony_ci __u64 rsvd[2]; 508c2ecf20Sopenharmony_ci __le64 prp1; 518c2ecf20Sopenharmony_ci __le64 prp2; 528c2ecf20Sopenharmony_ci __le64 spba; 538c2ecf20Sopenharmony_ci __le16 length; 548c2ecf20Sopenharmony_ci __le16 control; 558c2ecf20Sopenharmony_ci __le32 dsmgmt; 568c2ecf20Sopenharmony_ci __le64 resv; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct nvme_nvm_identity { 608c2ecf20Sopenharmony_ci __u8 opcode; 618c2ecf20Sopenharmony_ci __u8 flags; 628c2ecf20Sopenharmony_ci __u16 command_id; 638c2ecf20Sopenharmony_ci __le32 nsid; 648c2ecf20Sopenharmony_ci __u64 rsvd[2]; 658c2ecf20Sopenharmony_ci __le64 prp1; 668c2ecf20Sopenharmony_ci __le64 prp2; 678c2ecf20Sopenharmony_ci __u32 rsvd11[6]; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct nvme_nvm_getbbtbl { 718c2ecf20Sopenharmony_ci __u8 opcode; 728c2ecf20Sopenharmony_ci __u8 flags; 738c2ecf20Sopenharmony_ci __u16 command_id; 748c2ecf20Sopenharmony_ci __le32 nsid; 758c2ecf20Sopenharmony_ci __u64 rsvd[2]; 768c2ecf20Sopenharmony_ci __le64 prp1; 778c2ecf20Sopenharmony_ci __le64 prp2; 788c2ecf20Sopenharmony_ci __le64 spba; 798c2ecf20Sopenharmony_ci __u32 rsvd4[4]; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct nvme_nvm_setbbtbl { 838c2ecf20Sopenharmony_ci __u8 opcode; 848c2ecf20Sopenharmony_ci __u8 flags; 858c2ecf20Sopenharmony_ci __u16 command_id; 868c2ecf20Sopenharmony_ci __le32 nsid; 878c2ecf20Sopenharmony_ci __le64 rsvd[2]; 888c2ecf20Sopenharmony_ci __le64 prp1; 898c2ecf20Sopenharmony_ci __le64 prp2; 908c2ecf20Sopenharmony_ci __le64 spba; 918c2ecf20Sopenharmony_ci __le16 nlb; 928c2ecf20Sopenharmony_ci __u8 value; 938c2ecf20Sopenharmony_ci __u8 rsvd3; 948c2ecf20Sopenharmony_ci __u32 rsvd4[3]; 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct nvme_nvm_command { 988c2ecf20Sopenharmony_ci union { 998c2ecf20Sopenharmony_ci struct nvme_common_command common; 1008c2ecf20Sopenharmony_ci struct nvme_nvm_ph_rw ph_rw; 1018c2ecf20Sopenharmony_ci struct nvme_nvm_erase_blk erase; 1028c2ecf20Sopenharmony_ci struct nvme_nvm_identity identity; 1038c2ecf20Sopenharmony_ci struct nvme_nvm_getbbtbl get_bb; 1048c2ecf20Sopenharmony_ci struct nvme_nvm_setbbtbl set_bb; 1058c2ecf20Sopenharmony_ci }; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct nvme_nvm_id12_grp { 1098c2ecf20Sopenharmony_ci __u8 mtype; 1108c2ecf20Sopenharmony_ci __u8 fmtype; 1118c2ecf20Sopenharmony_ci __le16 res16; 1128c2ecf20Sopenharmony_ci __u8 num_ch; 1138c2ecf20Sopenharmony_ci __u8 num_lun; 1148c2ecf20Sopenharmony_ci __u8 num_pln; 1158c2ecf20Sopenharmony_ci __u8 rsvd1; 1168c2ecf20Sopenharmony_ci __le16 num_chk; 1178c2ecf20Sopenharmony_ci __le16 num_pg; 1188c2ecf20Sopenharmony_ci __le16 fpg_sz; 1198c2ecf20Sopenharmony_ci __le16 csecs; 1208c2ecf20Sopenharmony_ci __le16 sos; 1218c2ecf20Sopenharmony_ci __le16 rsvd2; 1228c2ecf20Sopenharmony_ci __le32 trdt; 1238c2ecf20Sopenharmony_ci __le32 trdm; 1248c2ecf20Sopenharmony_ci __le32 tprt; 1258c2ecf20Sopenharmony_ci __le32 tprm; 1268c2ecf20Sopenharmony_ci __le32 tbet; 1278c2ecf20Sopenharmony_ci __le32 tbem; 1288c2ecf20Sopenharmony_ci __le32 mpos; 1298c2ecf20Sopenharmony_ci __le32 mccap; 1308c2ecf20Sopenharmony_ci __le16 cpar; 1318c2ecf20Sopenharmony_ci __u8 reserved[906]; 1328c2ecf20Sopenharmony_ci} __packed; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistruct nvme_nvm_id12_addrf { 1358c2ecf20Sopenharmony_ci __u8 ch_offset; 1368c2ecf20Sopenharmony_ci __u8 ch_len; 1378c2ecf20Sopenharmony_ci __u8 lun_offset; 1388c2ecf20Sopenharmony_ci __u8 lun_len; 1398c2ecf20Sopenharmony_ci __u8 pln_offset; 1408c2ecf20Sopenharmony_ci __u8 pln_len; 1418c2ecf20Sopenharmony_ci __u8 blk_offset; 1428c2ecf20Sopenharmony_ci __u8 blk_len; 1438c2ecf20Sopenharmony_ci __u8 pg_offset; 1448c2ecf20Sopenharmony_ci __u8 pg_len; 1458c2ecf20Sopenharmony_ci __u8 sec_offset; 1468c2ecf20Sopenharmony_ci __u8 sec_len; 1478c2ecf20Sopenharmony_ci __u8 res[4]; 1488c2ecf20Sopenharmony_ci} __packed; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct nvme_nvm_id12 { 1518c2ecf20Sopenharmony_ci __u8 ver_id; 1528c2ecf20Sopenharmony_ci __u8 vmnt; 1538c2ecf20Sopenharmony_ci __u8 cgrps; 1548c2ecf20Sopenharmony_ci __u8 res; 1558c2ecf20Sopenharmony_ci __le32 cap; 1568c2ecf20Sopenharmony_ci __le32 dom; 1578c2ecf20Sopenharmony_ci struct nvme_nvm_id12_addrf ppaf; 1588c2ecf20Sopenharmony_ci __u8 resv[228]; 1598c2ecf20Sopenharmony_ci struct nvme_nvm_id12_grp grp; 1608c2ecf20Sopenharmony_ci __u8 resv2[2880]; 1618c2ecf20Sopenharmony_ci} __packed; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct nvme_nvm_bb_tbl { 1648c2ecf20Sopenharmony_ci __u8 tblid[4]; 1658c2ecf20Sopenharmony_ci __le16 verid; 1668c2ecf20Sopenharmony_ci __le16 revid; 1678c2ecf20Sopenharmony_ci __le32 rvsd1; 1688c2ecf20Sopenharmony_ci __le32 tblks; 1698c2ecf20Sopenharmony_ci __le32 tfact; 1708c2ecf20Sopenharmony_ci __le32 tgrown; 1718c2ecf20Sopenharmony_ci __le32 tdresv; 1728c2ecf20Sopenharmony_ci __le32 thresv; 1738c2ecf20Sopenharmony_ci __le32 rsvd2[8]; 1748c2ecf20Sopenharmony_ci __u8 blk[]; 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistruct nvme_nvm_id20_addrf { 1788c2ecf20Sopenharmony_ci __u8 grp_len; 1798c2ecf20Sopenharmony_ci __u8 pu_len; 1808c2ecf20Sopenharmony_ci __u8 chk_len; 1818c2ecf20Sopenharmony_ci __u8 lba_len; 1828c2ecf20Sopenharmony_ci __u8 resv[4]; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistruct nvme_nvm_id20 { 1868c2ecf20Sopenharmony_ci __u8 mjr; 1878c2ecf20Sopenharmony_ci __u8 mnr; 1888c2ecf20Sopenharmony_ci __u8 resv[6]; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci struct nvme_nvm_id20_addrf lbaf; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci __le32 mccap; 1938c2ecf20Sopenharmony_ci __u8 resv2[12]; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci __u8 wit; 1968c2ecf20Sopenharmony_ci __u8 resv3[31]; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Geometry */ 1998c2ecf20Sopenharmony_ci __le16 num_grp; 2008c2ecf20Sopenharmony_ci __le16 num_pu; 2018c2ecf20Sopenharmony_ci __le32 num_chk; 2028c2ecf20Sopenharmony_ci __le32 clba; 2038c2ecf20Sopenharmony_ci __u8 resv4[52]; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Write data requirements */ 2068c2ecf20Sopenharmony_ci __le32 ws_min; 2078c2ecf20Sopenharmony_ci __le32 ws_opt; 2088c2ecf20Sopenharmony_ci __le32 mw_cunits; 2098c2ecf20Sopenharmony_ci __le32 maxoc; 2108c2ecf20Sopenharmony_ci __le32 maxocpu; 2118c2ecf20Sopenharmony_ci __u8 resv5[44]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Performance related metrics */ 2148c2ecf20Sopenharmony_ci __le32 trdt; 2158c2ecf20Sopenharmony_ci __le32 trdm; 2168c2ecf20Sopenharmony_ci __le32 twrt; 2178c2ecf20Sopenharmony_ci __le32 twrm; 2188c2ecf20Sopenharmony_ci __le32 tcrst; 2198c2ecf20Sopenharmony_ci __le32 tcrsm; 2208c2ecf20Sopenharmony_ci __u8 resv6[40]; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Reserved area */ 2238c2ecf20Sopenharmony_ci __u8 resv7[2816]; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Vendor specific */ 2268c2ecf20Sopenharmony_ci __u8 vs[1024]; 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistruct nvme_nvm_chk_meta { 2308c2ecf20Sopenharmony_ci __u8 state; 2318c2ecf20Sopenharmony_ci __u8 type; 2328c2ecf20Sopenharmony_ci __u8 wi; 2338c2ecf20Sopenharmony_ci __u8 rsvd[5]; 2348c2ecf20Sopenharmony_ci __le64 slba; 2358c2ecf20Sopenharmony_ci __le64 cnlb; 2368c2ecf20Sopenharmony_ci __le64 wp; 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * Check we didn't inadvertently grow the command struct 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic inline void _nvme_nvm_check_size(void) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_identity) != 64); 2458c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_ph_rw) != 64); 2468c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_erase_blk) != 64); 2478c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_getbbtbl) != 64); 2488c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_setbbtbl) != 64); 2498c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_id12_grp) != 960); 2508c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_id12_addrf) != 16); 2518c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_id12) != NVME_IDENTIFY_DATA_SIZE); 2528c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_bb_tbl) != 64); 2538c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_id20_addrf) != 8); 2548c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_id20) != NVME_IDENTIFY_DATA_SIZE); 2558c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_chk_meta) != 32); 2568c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nvme_nvm_chk_meta) != 2578c2ecf20Sopenharmony_ci sizeof(struct nvm_chk_meta)); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void nvme_nvm_set_addr_12(struct nvm_addrf_12 *dst, 2618c2ecf20Sopenharmony_ci struct nvme_nvm_id12_addrf *src) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci dst->ch_len = src->ch_len; 2648c2ecf20Sopenharmony_ci dst->lun_len = src->lun_len; 2658c2ecf20Sopenharmony_ci dst->blk_len = src->blk_len; 2668c2ecf20Sopenharmony_ci dst->pg_len = src->pg_len; 2678c2ecf20Sopenharmony_ci dst->pln_len = src->pln_len; 2688c2ecf20Sopenharmony_ci dst->sec_len = src->sec_len; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci dst->ch_offset = src->ch_offset; 2718c2ecf20Sopenharmony_ci dst->lun_offset = src->lun_offset; 2728c2ecf20Sopenharmony_ci dst->blk_offset = src->blk_offset; 2738c2ecf20Sopenharmony_ci dst->pg_offset = src->pg_offset; 2748c2ecf20Sopenharmony_ci dst->pln_offset = src->pln_offset; 2758c2ecf20Sopenharmony_ci dst->sec_offset = src->sec_offset; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dst->ch_mask = ((1ULL << dst->ch_len) - 1) << dst->ch_offset; 2788c2ecf20Sopenharmony_ci dst->lun_mask = ((1ULL << dst->lun_len) - 1) << dst->lun_offset; 2798c2ecf20Sopenharmony_ci dst->blk_mask = ((1ULL << dst->blk_len) - 1) << dst->blk_offset; 2808c2ecf20Sopenharmony_ci dst->pg_mask = ((1ULL << dst->pg_len) - 1) << dst->pg_offset; 2818c2ecf20Sopenharmony_ci dst->pln_mask = ((1ULL << dst->pln_len) - 1) << dst->pln_offset; 2828c2ecf20Sopenharmony_ci dst->sec_mask = ((1ULL << dst->sec_len) - 1) << dst->sec_offset; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int nvme_nvm_setup_12(struct nvme_nvm_id12 *id, 2868c2ecf20Sopenharmony_ci struct nvm_geo *geo) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct nvme_nvm_id12_grp *src; 2898c2ecf20Sopenharmony_ci int sec_per_pg, sec_per_pl, pg_per_blk; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (id->cgrps != 1) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci src = &id->grp; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (src->mtype != 0) { 2978c2ecf20Sopenharmony_ci pr_err("nvm: memory type not supported\n"); 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 1.2 spec. only reports a single version id - unfold */ 3028c2ecf20Sopenharmony_ci geo->major_ver_id = id->ver_id; 3038c2ecf20Sopenharmony_ci geo->minor_ver_id = 2; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Set compacted version for upper layers */ 3068c2ecf20Sopenharmony_ci geo->version = NVM_OCSSD_SPEC_12; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci geo->num_ch = src->num_ch; 3098c2ecf20Sopenharmony_ci geo->num_lun = src->num_lun; 3108c2ecf20Sopenharmony_ci geo->all_luns = geo->num_ch * geo->num_lun; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci geo->num_chk = le16_to_cpu(src->num_chk); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci geo->csecs = le16_to_cpu(src->csecs); 3158c2ecf20Sopenharmony_ci geo->sos = le16_to_cpu(src->sos); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pg_per_blk = le16_to_cpu(src->num_pg); 3188c2ecf20Sopenharmony_ci sec_per_pg = le16_to_cpu(src->fpg_sz) / geo->csecs; 3198c2ecf20Sopenharmony_ci sec_per_pl = sec_per_pg * src->num_pln; 3208c2ecf20Sopenharmony_ci geo->clba = sec_per_pl * pg_per_blk; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci geo->all_chunks = geo->all_luns * geo->num_chk; 3238c2ecf20Sopenharmony_ci geo->total_secs = geo->clba * geo->all_chunks; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci geo->ws_min = sec_per_pg; 3268c2ecf20Sopenharmony_ci geo->ws_opt = sec_per_pg; 3278c2ecf20Sopenharmony_ci geo->mw_cunits = geo->ws_opt << 3; /* default to MLC safe values */ 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Do not impose values for maximum number of open blocks as it is 3308c2ecf20Sopenharmony_ci * unspecified in 1.2. Users of 1.2 must be aware of this and eventually 3318c2ecf20Sopenharmony_ci * specify these values through a quirk if restrictions apply. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci geo->maxoc = geo->all_luns * geo->num_chk; 3348c2ecf20Sopenharmony_ci geo->maxocpu = geo->num_chk; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci geo->mccap = le32_to_cpu(src->mccap); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci geo->trdt = le32_to_cpu(src->trdt); 3398c2ecf20Sopenharmony_ci geo->trdm = le32_to_cpu(src->trdm); 3408c2ecf20Sopenharmony_ci geo->tprt = le32_to_cpu(src->tprt); 3418c2ecf20Sopenharmony_ci geo->tprm = le32_to_cpu(src->tprm); 3428c2ecf20Sopenharmony_ci geo->tbet = le32_to_cpu(src->tbet); 3438c2ecf20Sopenharmony_ci geo->tbem = le32_to_cpu(src->tbem); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* 1.2 compatibility */ 3468c2ecf20Sopenharmony_ci geo->vmnt = id->vmnt; 3478c2ecf20Sopenharmony_ci geo->cap = le32_to_cpu(id->cap); 3488c2ecf20Sopenharmony_ci geo->dom = le32_to_cpu(id->dom); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci geo->mtype = src->mtype; 3518c2ecf20Sopenharmony_ci geo->fmtype = src->fmtype; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci geo->cpar = le16_to_cpu(src->cpar); 3548c2ecf20Sopenharmony_ci geo->mpos = le32_to_cpu(src->mpos); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci geo->pln_mode = NVM_PLANE_SINGLE; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (geo->mpos & 0x020202) { 3598c2ecf20Sopenharmony_ci geo->pln_mode = NVM_PLANE_DOUBLE; 3608c2ecf20Sopenharmony_ci geo->ws_opt <<= 1; 3618c2ecf20Sopenharmony_ci } else if (geo->mpos & 0x040404) { 3628c2ecf20Sopenharmony_ci geo->pln_mode = NVM_PLANE_QUAD; 3638c2ecf20Sopenharmony_ci geo->ws_opt <<= 2; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci geo->num_pln = src->num_pln; 3678c2ecf20Sopenharmony_ci geo->num_pg = le16_to_cpu(src->num_pg); 3688c2ecf20Sopenharmony_ci geo->fpg_sz = le16_to_cpu(src->fpg_sz); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci nvme_nvm_set_addr_12((struct nvm_addrf_12 *)&geo->addrf, &id->ppaf); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void nvme_nvm_set_addr_20(struct nvm_addrf *dst, 3768c2ecf20Sopenharmony_ci struct nvme_nvm_id20_addrf *src) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci dst->ch_len = src->grp_len; 3798c2ecf20Sopenharmony_ci dst->lun_len = src->pu_len; 3808c2ecf20Sopenharmony_ci dst->chk_len = src->chk_len; 3818c2ecf20Sopenharmony_ci dst->sec_len = src->lba_len; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci dst->sec_offset = 0; 3848c2ecf20Sopenharmony_ci dst->chk_offset = dst->sec_len; 3858c2ecf20Sopenharmony_ci dst->lun_offset = dst->chk_offset + dst->chk_len; 3868c2ecf20Sopenharmony_ci dst->ch_offset = dst->lun_offset + dst->lun_len; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dst->ch_mask = ((1ULL << dst->ch_len) - 1) << dst->ch_offset; 3898c2ecf20Sopenharmony_ci dst->lun_mask = ((1ULL << dst->lun_len) - 1) << dst->lun_offset; 3908c2ecf20Sopenharmony_ci dst->chk_mask = ((1ULL << dst->chk_len) - 1) << dst->chk_offset; 3918c2ecf20Sopenharmony_ci dst->sec_mask = ((1ULL << dst->sec_len) - 1) << dst->sec_offset; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int nvme_nvm_setup_20(struct nvme_nvm_id20 *id, 3958c2ecf20Sopenharmony_ci struct nvm_geo *geo) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci geo->major_ver_id = id->mjr; 3988c2ecf20Sopenharmony_ci geo->minor_ver_id = id->mnr; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Set compacted version for upper layers */ 4018c2ecf20Sopenharmony_ci geo->version = NVM_OCSSD_SPEC_20; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci geo->num_ch = le16_to_cpu(id->num_grp); 4048c2ecf20Sopenharmony_ci geo->num_lun = le16_to_cpu(id->num_pu); 4058c2ecf20Sopenharmony_ci geo->all_luns = geo->num_ch * geo->num_lun; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci geo->num_chk = le32_to_cpu(id->num_chk); 4088c2ecf20Sopenharmony_ci geo->clba = le32_to_cpu(id->clba); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci geo->all_chunks = geo->all_luns * geo->num_chk; 4118c2ecf20Sopenharmony_ci geo->total_secs = geo->clba * geo->all_chunks; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci geo->ws_min = le32_to_cpu(id->ws_min); 4148c2ecf20Sopenharmony_ci geo->ws_opt = le32_to_cpu(id->ws_opt); 4158c2ecf20Sopenharmony_ci geo->mw_cunits = le32_to_cpu(id->mw_cunits); 4168c2ecf20Sopenharmony_ci geo->maxoc = le32_to_cpu(id->maxoc); 4178c2ecf20Sopenharmony_ci geo->maxocpu = le32_to_cpu(id->maxocpu); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci geo->trdt = le32_to_cpu(id->trdt); 4208c2ecf20Sopenharmony_ci geo->trdm = le32_to_cpu(id->trdm); 4218c2ecf20Sopenharmony_ci geo->tprt = le32_to_cpu(id->twrt); 4228c2ecf20Sopenharmony_ci geo->tprm = le32_to_cpu(id->twrm); 4238c2ecf20Sopenharmony_ci geo->tbet = le32_to_cpu(id->tcrst); 4248c2ecf20Sopenharmony_ci geo->tbem = le32_to_cpu(id->tcrsm); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci nvme_nvm_set_addr_20(&geo->addrf, &id->lbaf); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int nvme_nvm_identity(struct nvm_dev *nvmdev) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvmdev->q->queuedata; 4348c2ecf20Sopenharmony_ci struct nvme_nvm_id12 *id; 4358c2ecf20Sopenharmony_ci struct nvme_nvm_command c = {}; 4368c2ecf20Sopenharmony_ci int ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci c.identity.opcode = nvme_nvm_admin_identity; 4398c2ecf20Sopenharmony_ci c.identity.nsid = cpu_to_le32(ns->head->ns_id); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci id = kmalloc(sizeof(struct nvme_nvm_id12), GFP_KERNEL); 4428c2ecf20Sopenharmony_ci if (!id) 4438c2ecf20Sopenharmony_ci return -ENOMEM; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ret = nvme_submit_sync_cmd(ns->ctrl->admin_q, (struct nvme_command *)&c, 4468c2ecf20Sopenharmony_ci id, sizeof(struct nvme_nvm_id12)); 4478c2ecf20Sopenharmony_ci if (ret) { 4488c2ecf20Sopenharmony_ci ret = -EIO; 4498c2ecf20Sopenharmony_ci goto out; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* 4538c2ecf20Sopenharmony_ci * The 1.2 and 2.0 specifications share the first byte in their geometry 4548c2ecf20Sopenharmony_ci * command to make it possible to know what version a device implements. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci switch (id->ver_id) { 4578c2ecf20Sopenharmony_ci case 1: 4588c2ecf20Sopenharmony_ci ret = nvme_nvm_setup_12(id, &nvmdev->geo); 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case 2: 4618c2ecf20Sopenharmony_ci ret = nvme_nvm_setup_20((struct nvme_nvm_id20 *)id, 4628c2ecf20Sopenharmony_ci &nvmdev->geo); 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci default: 4658c2ecf20Sopenharmony_ci dev_err(ns->ctrl->device, "OCSSD revision not supported (%d)\n", 4668c2ecf20Sopenharmony_ci id->ver_id); 4678c2ecf20Sopenharmony_ci ret = -EINVAL; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ciout: 4718c2ecf20Sopenharmony_ci kfree(id); 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa, 4768c2ecf20Sopenharmony_ci u8 *blks) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct request_queue *q = nvmdev->q; 4798c2ecf20Sopenharmony_ci struct nvm_geo *geo = &nvmdev->geo; 4808c2ecf20Sopenharmony_ci struct nvme_ns *ns = q->queuedata; 4818c2ecf20Sopenharmony_ci struct nvme_ctrl *ctrl = ns->ctrl; 4828c2ecf20Sopenharmony_ci struct nvme_nvm_command c = {}; 4838c2ecf20Sopenharmony_ci struct nvme_nvm_bb_tbl *bb_tbl; 4848c2ecf20Sopenharmony_ci int nr_blks = geo->num_chk * geo->num_pln; 4858c2ecf20Sopenharmony_ci int tblsz = sizeof(struct nvme_nvm_bb_tbl) + nr_blks; 4868c2ecf20Sopenharmony_ci int ret = 0; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci c.get_bb.opcode = nvme_nvm_admin_get_bb_tbl; 4898c2ecf20Sopenharmony_ci c.get_bb.nsid = cpu_to_le32(ns->head->ns_id); 4908c2ecf20Sopenharmony_ci c.get_bb.spba = cpu_to_le64(ppa.ppa); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci bb_tbl = kzalloc(tblsz, GFP_KERNEL); 4938c2ecf20Sopenharmony_ci if (!bb_tbl) 4948c2ecf20Sopenharmony_ci return -ENOMEM; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ret = nvme_submit_sync_cmd(ctrl->admin_q, (struct nvme_command *)&c, 4978c2ecf20Sopenharmony_ci bb_tbl, tblsz); 4988c2ecf20Sopenharmony_ci if (ret) { 4998c2ecf20Sopenharmony_ci dev_err(ctrl->device, "get bad block table failed (%d)\n", ret); 5008c2ecf20Sopenharmony_ci ret = -EIO; 5018c2ecf20Sopenharmony_ci goto out; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (bb_tbl->tblid[0] != 'B' || bb_tbl->tblid[1] != 'B' || 5058c2ecf20Sopenharmony_ci bb_tbl->tblid[2] != 'L' || bb_tbl->tblid[3] != 'T') { 5068c2ecf20Sopenharmony_ci dev_err(ctrl->device, "bbt format mismatch\n"); 5078c2ecf20Sopenharmony_ci ret = -EINVAL; 5088c2ecf20Sopenharmony_ci goto out; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (le16_to_cpu(bb_tbl->verid) != 1) { 5128c2ecf20Sopenharmony_ci ret = -EINVAL; 5138c2ecf20Sopenharmony_ci dev_err(ctrl->device, "bbt version not supported\n"); 5148c2ecf20Sopenharmony_ci goto out; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (le32_to_cpu(bb_tbl->tblks) != nr_blks) { 5188c2ecf20Sopenharmony_ci ret = -EINVAL; 5198c2ecf20Sopenharmony_ci dev_err(ctrl->device, 5208c2ecf20Sopenharmony_ci "bbt unsuspected blocks returned (%u!=%u)", 5218c2ecf20Sopenharmony_ci le32_to_cpu(bb_tbl->tblks), nr_blks); 5228c2ecf20Sopenharmony_ci goto out; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci memcpy(blks, bb_tbl->blk, geo->num_chk * geo->num_pln); 5268c2ecf20Sopenharmony_ciout: 5278c2ecf20Sopenharmony_ci kfree(bb_tbl); 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr *ppas, 5328c2ecf20Sopenharmony_ci int nr_ppas, int type) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvmdev->q->queuedata; 5358c2ecf20Sopenharmony_ci struct nvme_nvm_command c = {}; 5368c2ecf20Sopenharmony_ci int ret = 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci c.set_bb.opcode = nvme_nvm_admin_set_bb_tbl; 5398c2ecf20Sopenharmony_ci c.set_bb.nsid = cpu_to_le32(ns->head->ns_id); 5408c2ecf20Sopenharmony_ci c.set_bb.spba = cpu_to_le64(ppas->ppa); 5418c2ecf20Sopenharmony_ci c.set_bb.nlb = cpu_to_le16(nr_ppas - 1); 5428c2ecf20Sopenharmony_ci c.set_bb.value = type; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = nvme_submit_sync_cmd(ns->ctrl->admin_q, (struct nvme_command *)&c, 5458c2ecf20Sopenharmony_ci NULL, 0); 5468c2ecf20Sopenharmony_ci if (ret) 5478c2ecf20Sopenharmony_ci dev_err(ns->ctrl->device, "set bad block table failed (%d)\n", 5488c2ecf20Sopenharmony_ci ret); 5498c2ecf20Sopenharmony_ci return ret; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* 5538c2ecf20Sopenharmony_ci * Expect the lba in device format 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_cistatic int nvme_nvm_get_chk_meta(struct nvm_dev *ndev, 5568c2ecf20Sopenharmony_ci sector_t slba, int nchks, 5578c2ecf20Sopenharmony_ci struct nvm_chk_meta *meta) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct nvm_geo *geo = &ndev->geo; 5608c2ecf20Sopenharmony_ci struct nvme_ns *ns = ndev->q->queuedata; 5618c2ecf20Sopenharmony_ci struct nvme_ctrl *ctrl = ns->ctrl; 5628c2ecf20Sopenharmony_ci struct nvme_nvm_chk_meta *dev_meta, *dev_meta_off; 5638c2ecf20Sopenharmony_ci struct ppa_addr ppa; 5648c2ecf20Sopenharmony_ci size_t left = nchks * sizeof(struct nvme_nvm_chk_meta); 5658c2ecf20Sopenharmony_ci size_t log_pos, offset, len; 5668c2ecf20Sopenharmony_ci int i, max_len; 5678c2ecf20Sopenharmony_ci int ret = 0; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * limit requests to maximum 256K to avoid issuing arbitrary large 5718c2ecf20Sopenharmony_ci * requests when the device does not specific a maximum transfer size. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci max_len = min_t(unsigned int, ctrl->max_hw_sectors << 9, 256 * 1024); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci dev_meta = kmalloc(max_len, GFP_KERNEL); 5768c2ecf20Sopenharmony_ci if (!dev_meta) 5778c2ecf20Sopenharmony_ci return -ENOMEM; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Normalize lba address space to obtain log offset */ 5808c2ecf20Sopenharmony_ci ppa.ppa = slba; 5818c2ecf20Sopenharmony_ci ppa = dev_to_generic_addr(ndev, ppa); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci log_pos = ppa.m.chk; 5848c2ecf20Sopenharmony_ci log_pos += ppa.m.pu * geo->num_chk; 5858c2ecf20Sopenharmony_ci log_pos += ppa.m.grp * geo->num_lun * geo->num_chk; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci offset = log_pos * sizeof(struct nvme_nvm_chk_meta); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci while (left) { 5908c2ecf20Sopenharmony_ci len = min_t(unsigned int, left, max_len); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci memset(dev_meta, 0, max_len); 5938c2ecf20Sopenharmony_ci dev_meta_off = dev_meta; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci ret = nvme_get_log(ctrl, ns->head->ns_id, 5968c2ecf20Sopenharmony_ci NVME_NVM_LOG_REPORT_CHUNK, 0, NVME_CSI_NVM, 5978c2ecf20Sopenharmony_ci dev_meta, len, offset); 5988c2ecf20Sopenharmony_ci if (ret) { 5998c2ecf20Sopenharmony_ci dev_err(ctrl->device, "Get REPORT CHUNK log error\n"); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci for (i = 0; i < len; i += sizeof(struct nvme_nvm_chk_meta)) { 6048c2ecf20Sopenharmony_ci meta->state = dev_meta_off->state; 6058c2ecf20Sopenharmony_ci meta->type = dev_meta_off->type; 6068c2ecf20Sopenharmony_ci meta->wi = dev_meta_off->wi; 6078c2ecf20Sopenharmony_ci meta->slba = le64_to_cpu(dev_meta_off->slba); 6088c2ecf20Sopenharmony_ci meta->cnlb = le64_to_cpu(dev_meta_off->cnlb); 6098c2ecf20Sopenharmony_ci meta->wp = le64_to_cpu(dev_meta_off->wp); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci meta++; 6128c2ecf20Sopenharmony_ci dev_meta_off++; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci offset += len; 6168c2ecf20Sopenharmony_ci left -= len; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci kfree(dev_meta); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic inline void nvme_nvm_rqtocmd(struct nvm_rq *rqd, struct nvme_ns *ns, 6258c2ecf20Sopenharmony_ci struct nvme_nvm_command *c) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci c->ph_rw.opcode = rqd->opcode; 6288c2ecf20Sopenharmony_ci c->ph_rw.nsid = cpu_to_le32(ns->head->ns_id); 6298c2ecf20Sopenharmony_ci c->ph_rw.spba = cpu_to_le64(rqd->ppa_addr.ppa); 6308c2ecf20Sopenharmony_ci c->ph_rw.metadata = cpu_to_le64(rqd->dma_meta_list); 6318c2ecf20Sopenharmony_ci c->ph_rw.control = cpu_to_le16(rqd->flags); 6328c2ecf20Sopenharmony_ci c->ph_rw.length = cpu_to_le16(rqd->nr_ppas - 1); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void nvme_nvm_end_io(struct request *rq, blk_status_t status) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct nvm_rq *rqd = rq->end_io_data; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci rqd->ppa_status = le64_to_cpu(nvme_req(rq)->result.u64); 6408c2ecf20Sopenharmony_ci rqd->error = nvme_req(rq)->status; 6418c2ecf20Sopenharmony_ci nvm_end_io(rqd); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci kfree(nvme_req(rq)->cmd); 6448c2ecf20Sopenharmony_ci blk_mq_free_request(rq); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic struct request *nvme_nvm_alloc_request(struct request_queue *q, 6488c2ecf20Sopenharmony_ci struct nvm_rq *rqd, 6498c2ecf20Sopenharmony_ci struct nvme_nvm_command *cmd) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct nvme_ns *ns = q->queuedata; 6528c2ecf20Sopenharmony_ci struct request *rq; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci nvme_nvm_rqtocmd(rqd, ns, cmd); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci rq = nvme_alloc_request(q, (struct nvme_command *)cmd, 0); 6578c2ecf20Sopenharmony_ci if (IS_ERR(rq)) 6588c2ecf20Sopenharmony_ci return rq; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci rq->cmd_flags &= ~REQ_FAILFAST_DRIVER; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (rqd->bio) 6638c2ecf20Sopenharmony_ci blk_rq_append_bio(rq, &rqd->bio); 6648c2ecf20Sopenharmony_ci else 6658c2ecf20Sopenharmony_ci rq->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return rq; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int nvme_nvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd, 6718c2ecf20Sopenharmony_ci void *buf) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct nvm_geo *geo = &dev->geo; 6748c2ecf20Sopenharmony_ci struct request_queue *q = dev->q; 6758c2ecf20Sopenharmony_ci struct nvme_nvm_command *cmd; 6768c2ecf20Sopenharmony_ci struct request *rq; 6778c2ecf20Sopenharmony_ci int ret; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(struct nvme_nvm_command), GFP_KERNEL); 6808c2ecf20Sopenharmony_ci if (!cmd) 6818c2ecf20Sopenharmony_ci return -ENOMEM; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci rq = nvme_nvm_alloc_request(q, rqd, cmd); 6848c2ecf20Sopenharmony_ci if (IS_ERR(rq)) { 6858c2ecf20Sopenharmony_ci ret = PTR_ERR(rq); 6868c2ecf20Sopenharmony_ci goto err_free_cmd; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (buf) { 6908c2ecf20Sopenharmony_ci ret = blk_rq_map_kern(q, rq, buf, geo->csecs * rqd->nr_ppas, 6918c2ecf20Sopenharmony_ci GFP_KERNEL); 6928c2ecf20Sopenharmony_ci if (ret) 6938c2ecf20Sopenharmony_ci goto err_free_cmd; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci rq->end_io_data = rqd; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci blk_execute_rq_nowait(q, NULL, rq, 0, nvme_nvm_end_io); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cierr_free_cmd: 7038c2ecf20Sopenharmony_ci kfree(cmd); 7048c2ecf20Sopenharmony_ci return ret; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void *nvme_nvm_create_dma_pool(struct nvm_dev *nvmdev, char *name, 7088c2ecf20Sopenharmony_ci int size) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvmdev->q->queuedata; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return dma_pool_create(name, ns->ctrl->dev, size, PAGE_SIZE, 0); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void nvme_nvm_destroy_dma_pool(void *pool) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct dma_pool *dma_pool = pool; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci dma_pool_destroy(dma_pool); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic void *nvme_nvm_dev_dma_alloc(struct nvm_dev *dev, void *pool, 7238c2ecf20Sopenharmony_ci gfp_t mem_flags, dma_addr_t *dma_handler) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci return dma_pool_alloc(pool, mem_flags, dma_handler); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic void nvme_nvm_dev_dma_free(void *pool, void *addr, 7298c2ecf20Sopenharmony_ci dma_addr_t dma_handler) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci dma_pool_free(pool, addr, dma_handler); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic struct nvm_dev_ops nvme_nvm_dev_ops = { 7358c2ecf20Sopenharmony_ci .identity = nvme_nvm_identity, 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci .get_bb_tbl = nvme_nvm_get_bb_tbl, 7388c2ecf20Sopenharmony_ci .set_bb_tbl = nvme_nvm_set_bb_tbl, 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci .get_chk_meta = nvme_nvm_get_chk_meta, 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci .submit_io = nvme_nvm_submit_io, 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci .create_dma_pool = nvme_nvm_create_dma_pool, 7458c2ecf20Sopenharmony_ci .destroy_dma_pool = nvme_nvm_destroy_dma_pool, 7468c2ecf20Sopenharmony_ci .dev_dma_alloc = nvme_nvm_dev_dma_alloc, 7478c2ecf20Sopenharmony_ci .dev_dma_free = nvme_nvm_dev_dma_free, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int nvme_nvm_submit_user_cmd(struct request_queue *q, 7518c2ecf20Sopenharmony_ci struct nvme_ns *ns, 7528c2ecf20Sopenharmony_ci struct nvme_nvm_command *vcmd, 7538c2ecf20Sopenharmony_ci void __user *ubuf, unsigned int bufflen, 7548c2ecf20Sopenharmony_ci void __user *meta_buf, unsigned int meta_len, 7558c2ecf20Sopenharmony_ci void __user *ppa_buf, unsigned int ppa_len, 7568c2ecf20Sopenharmony_ci u32 *result, u64 *status, unsigned int timeout) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci bool write = nvme_is_write((struct nvme_command *)vcmd); 7598c2ecf20Sopenharmony_ci struct nvm_dev *dev = ns->ndev; 7608c2ecf20Sopenharmony_ci struct gendisk *disk = ns->disk; 7618c2ecf20Sopenharmony_ci struct request *rq; 7628c2ecf20Sopenharmony_ci struct bio *bio = NULL; 7638c2ecf20Sopenharmony_ci __le64 *ppa_list = NULL; 7648c2ecf20Sopenharmony_ci dma_addr_t ppa_dma; 7658c2ecf20Sopenharmony_ci __le64 *metadata = NULL; 7668c2ecf20Sopenharmony_ci dma_addr_t metadata_dma; 7678c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(wait); 7688c2ecf20Sopenharmony_ci int ret = 0; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci rq = nvme_alloc_request(q, (struct nvme_command *)vcmd, 0); 7718c2ecf20Sopenharmony_ci if (IS_ERR(rq)) { 7728c2ecf20Sopenharmony_ci ret = -ENOMEM; 7738c2ecf20Sopenharmony_ci goto err_cmd; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (timeout) 7778c2ecf20Sopenharmony_ci rq->timeout = timeout; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (ppa_buf && ppa_len) { 7808c2ecf20Sopenharmony_ci ppa_list = dma_pool_alloc(dev->dma_pool, GFP_KERNEL, &ppa_dma); 7818c2ecf20Sopenharmony_ci if (!ppa_list) { 7828c2ecf20Sopenharmony_ci ret = -ENOMEM; 7838c2ecf20Sopenharmony_ci goto err_rq; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci if (copy_from_user(ppa_list, (void __user *)ppa_buf, 7868c2ecf20Sopenharmony_ci sizeof(u64) * (ppa_len + 1))) { 7878c2ecf20Sopenharmony_ci ret = -EFAULT; 7888c2ecf20Sopenharmony_ci goto err_ppa; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci vcmd->ph_rw.spba = cpu_to_le64(ppa_dma); 7918c2ecf20Sopenharmony_ci } else { 7928c2ecf20Sopenharmony_ci vcmd->ph_rw.spba = cpu_to_le64((uintptr_t)ppa_buf); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (ubuf && bufflen) { 7968c2ecf20Sopenharmony_ci ret = blk_rq_map_user(q, rq, NULL, ubuf, bufflen, GFP_KERNEL); 7978c2ecf20Sopenharmony_ci if (ret) 7988c2ecf20Sopenharmony_ci goto err_ppa; 7998c2ecf20Sopenharmony_ci bio = rq->bio; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (meta_buf && meta_len) { 8028c2ecf20Sopenharmony_ci metadata = dma_pool_alloc(dev->dma_pool, GFP_KERNEL, 8038c2ecf20Sopenharmony_ci &metadata_dma); 8048c2ecf20Sopenharmony_ci if (!metadata) { 8058c2ecf20Sopenharmony_ci ret = -ENOMEM; 8068c2ecf20Sopenharmony_ci goto err_map; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (write) { 8108c2ecf20Sopenharmony_ci if (copy_from_user(metadata, 8118c2ecf20Sopenharmony_ci (void __user *)meta_buf, 8128c2ecf20Sopenharmony_ci meta_len)) { 8138c2ecf20Sopenharmony_ci ret = -EFAULT; 8148c2ecf20Sopenharmony_ci goto err_meta; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci vcmd->ph_rw.metadata = cpu_to_le64(metadata_dma); 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci bio->bi_disk = disk; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci blk_execute_rq(q, NULL, rq, 0); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (nvme_req(rq)->flags & NVME_REQ_CANCELLED) 8268c2ecf20Sopenharmony_ci ret = -EINTR; 8278c2ecf20Sopenharmony_ci else if (nvme_req(rq)->status & 0x7ff) 8288c2ecf20Sopenharmony_ci ret = -EIO; 8298c2ecf20Sopenharmony_ci if (result) 8308c2ecf20Sopenharmony_ci *result = nvme_req(rq)->status & 0x7ff; 8318c2ecf20Sopenharmony_ci if (status) 8328c2ecf20Sopenharmony_ci *status = le64_to_cpu(nvme_req(rq)->result.u64); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (metadata && !ret && !write) { 8358c2ecf20Sopenharmony_ci if (copy_to_user(meta_buf, (void *)metadata, meta_len)) 8368c2ecf20Sopenharmony_ci ret = -EFAULT; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_cierr_meta: 8398c2ecf20Sopenharmony_ci if (meta_buf && meta_len) 8408c2ecf20Sopenharmony_ci dma_pool_free(dev->dma_pool, metadata, metadata_dma); 8418c2ecf20Sopenharmony_cierr_map: 8428c2ecf20Sopenharmony_ci if (bio) 8438c2ecf20Sopenharmony_ci blk_rq_unmap_user(bio); 8448c2ecf20Sopenharmony_cierr_ppa: 8458c2ecf20Sopenharmony_ci if (ppa_buf && ppa_len) 8468c2ecf20Sopenharmony_ci dma_pool_free(dev->dma_pool, ppa_list, ppa_dma); 8478c2ecf20Sopenharmony_cierr_rq: 8488c2ecf20Sopenharmony_ci blk_mq_free_request(rq); 8498c2ecf20Sopenharmony_cierr_cmd: 8508c2ecf20Sopenharmony_ci return ret; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic int nvme_nvm_submit_vio(struct nvme_ns *ns, 8548c2ecf20Sopenharmony_ci struct nvm_user_vio __user *uvio) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct nvm_user_vio vio; 8578c2ecf20Sopenharmony_ci struct nvme_nvm_command c; 8588c2ecf20Sopenharmony_ci unsigned int length; 8598c2ecf20Sopenharmony_ci int ret; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (copy_from_user(&vio, uvio, sizeof(vio))) 8628c2ecf20Sopenharmony_ci return -EFAULT; 8638c2ecf20Sopenharmony_ci if (vio.flags) 8648c2ecf20Sopenharmony_ci return -EINVAL; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci memset(&c, 0, sizeof(c)); 8678c2ecf20Sopenharmony_ci c.ph_rw.opcode = vio.opcode; 8688c2ecf20Sopenharmony_ci c.ph_rw.nsid = cpu_to_le32(ns->head->ns_id); 8698c2ecf20Sopenharmony_ci c.ph_rw.control = cpu_to_le16(vio.control); 8708c2ecf20Sopenharmony_ci c.ph_rw.length = cpu_to_le16(vio.nppas); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci length = (vio.nppas + 1) << ns->lba_shift; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci ret = nvme_nvm_submit_user_cmd(ns->queue, ns, &c, 8758c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vio.addr, length, 8768c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vio.metadata, 8778c2ecf20Sopenharmony_ci vio.metadata_len, 8788c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vio.ppa_list, vio.nppas, 8798c2ecf20Sopenharmony_ci &vio.result, &vio.status, 0); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (ret && copy_to_user(uvio, &vio, sizeof(vio))) 8828c2ecf20Sopenharmony_ci return -EFAULT; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci return ret; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic int nvme_nvm_user_vcmd(struct nvme_ns *ns, int admin, 8888c2ecf20Sopenharmony_ci struct nvm_passthru_vio __user *uvcmd) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct nvm_passthru_vio vcmd; 8918c2ecf20Sopenharmony_ci struct nvme_nvm_command c; 8928c2ecf20Sopenharmony_ci struct request_queue *q; 8938c2ecf20Sopenharmony_ci unsigned int timeout = 0; 8948c2ecf20Sopenharmony_ci int ret; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (copy_from_user(&vcmd, uvcmd, sizeof(vcmd))) 8978c2ecf20Sopenharmony_ci return -EFAULT; 8988c2ecf20Sopenharmony_ci if ((vcmd.opcode != 0xF2) && (!capable(CAP_SYS_ADMIN))) 8998c2ecf20Sopenharmony_ci return -EACCES; 9008c2ecf20Sopenharmony_ci if (vcmd.flags) 9018c2ecf20Sopenharmony_ci return -EINVAL; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci memset(&c, 0, sizeof(c)); 9048c2ecf20Sopenharmony_ci c.common.opcode = vcmd.opcode; 9058c2ecf20Sopenharmony_ci c.common.nsid = cpu_to_le32(ns->head->ns_id); 9068c2ecf20Sopenharmony_ci c.common.cdw2[0] = cpu_to_le32(vcmd.cdw2); 9078c2ecf20Sopenharmony_ci c.common.cdw2[1] = cpu_to_le32(vcmd.cdw3); 9088c2ecf20Sopenharmony_ci /* cdw11-12 */ 9098c2ecf20Sopenharmony_ci c.ph_rw.length = cpu_to_le16(vcmd.nppas); 9108c2ecf20Sopenharmony_ci c.ph_rw.control = cpu_to_le16(vcmd.control); 9118c2ecf20Sopenharmony_ci c.common.cdw13 = cpu_to_le32(vcmd.cdw13); 9128c2ecf20Sopenharmony_ci c.common.cdw14 = cpu_to_le32(vcmd.cdw14); 9138c2ecf20Sopenharmony_ci c.common.cdw15 = cpu_to_le32(vcmd.cdw15); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (vcmd.timeout_ms) 9168c2ecf20Sopenharmony_ci timeout = msecs_to_jiffies(vcmd.timeout_ms); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci q = admin ? ns->ctrl->admin_q : ns->queue; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ret = nvme_nvm_submit_user_cmd(q, ns, 9218c2ecf20Sopenharmony_ci (struct nvme_nvm_command *)&c, 9228c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vcmd.addr, vcmd.data_len, 9238c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vcmd.metadata, 9248c2ecf20Sopenharmony_ci vcmd.metadata_len, 9258c2ecf20Sopenharmony_ci (void __user *)(uintptr_t)vcmd.ppa_list, vcmd.nppas, 9268c2ecf20Sopenharmony_ci &vcmd.result, &vcmd.status, timeout); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (ret && copy_to_user(uvcmd, &vcmd, sizeof(vcmd))) 9298c2ecf20Sopenharmony_ci return -EFAULT; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return ret; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciint nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci switch (cmd) { 9378c2ecf20Sopenharmony_ci case NVME_NVM_IOCTL_ADMIN_VIO: 9388c2ecf20Sopenharmony_ci return nvme_nvm_user_vcmd(ns, 1, (void __user *)arg); 9398c2ecf20Sopenharmony_ci case NVME_NVM_IOCTL_IO_VIO: 9408c2ecf20Sopenharmony_ci return nvme_nvm_user_vcmd(ns, 0, (void __user *)arg); 9418c2ecf20Sopenharmony_ci case NVME_NVM_IOCTL_SUBMIT_VIO: 9428c2ecf20Sopenharmony_ci return nvme_nvm_submit_vio(ns, (void __user *)arg); 9438c2ecf20Sopenharmony_ci default: 9448c2ecf20Sopenharmony_ci return -ENOTTY; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ciint nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct request_queue *q = ns->queue; 9518c2ecf20Sopenharmony_ci struct nvm_dev *dev; 9528c2ecf20Sopenharmony_ci struct nvm_geo *geo; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci _nvme_nvm_check_size(); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci dev = nvm_alloc_dev(node); 9578c2ecf20Sopenharmony_ci if (!dev) 9588c2ecf20Sopenharmony_ci return -ENOMEM; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Note that csecs and sos will be overridden if it is a 1.2 drive. */ 9618c2ecf20Sopenharmony_ci geo = &dev->geo; 9628c2ecf20Sopenharmony_ci geo->csecs = 1 << ns->lba_shift; 9638c2ecf20Sopenharmony_ci geo->sos = ns->ms; 9648c2ecf20Sopenharmony_ci if (ns->features & NVME_NS_EXT_LBAS) 9658c2ecf20Sopenharmony_ci geo->ext = true; 9668c2ecf20Sopenharmony_ci else 9678c2ecf20Sopenharmony_ci geo->ext = false; 9688c2ecf20Sopenharmony_ci geo->mdts = ns->ctrl->max_hw_sectors; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci dev->q = q; 9718c2ecf20Sopenharmony_ci memcpy(dev->name, disk_name, DISK_NAME_LEN); 9728c2ecf20Sopenharmony_ci dev->ops = &nvme_nvm_dev_ops; 9738c2ecf20Sopenharmony_ci dev->private_data = ns; 9748c2ecf20Sopenharmony_ci ns->ndev = dev; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return nvm_register(dev); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_civoid nvme_nvm_unregister(struct nvme_ns *ns) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci nvm_unregister(ns->ndev); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic ssize_t nvm_dev_attr_show(struct device *dev, 9858c2ecf20Sopenharmony_ci struct device_attribute *dattr, char *page) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvme_get_ns_from_dev(dev); 9888c2ecf20Sopenharmony_ci struct nvm_dev *ndev = ns->ndev; 9898c2ecf20Sopenharmony_ci struct nvm_geo *geo = &ndev->geo; 9908c2ecf20Sopenharmony_ci struct attribute *attr; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (!ndev) 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci attr = &dattr->attr; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (strcmp(attr->name, "version") == 0) { 9988c2ecf20Sopenharmony_ci if (geo->major_ver_id == 1) 9998c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", 10008c2ecf20Sopenharmony_ci geo->major_ver_id); 10018c2ecf20Sopenharmony_ci else 10028c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u.%u\n", 10038c2ecf20Sopenharmony_ci geo->major_ver_id, 10048c2ecf20Sopenharmony_ci geo->minor_ver_id); 10058c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "capabilities") == 0) { 10068c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->cap); 10078c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "read_typ") == 0) { 10088c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->trdt); 10098c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "read_max") == 0) { 10108c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->trdm); 10118c2ecf20Sopenharmony_ci } else { 10128c2ecf20Sopenharmony_ci return scnprintf(page, 10138c2ecf20Sopenharmony_ci PAGE_SIZE, 10148c2ecf20Sopenharmony_ci "Unhandled attr(%s) in `%s`\n", 10158c2ecf20Sopenharmony_ci attr->name, __func__); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic ssize_t nvm_dev_attr_show_ppaf(struct nvm_addrf_12 *ppaf, char *page) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, 10228c2ecf20Sopenharmony_ci "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 10238c2ecf20Sopenharmony_ci ppaf->ch_offset, ppaf->ch_len, 10248c2ecf20Sopenharmony_ci ppaf->lun_offset, ppaf->lun_len, 10258c2ecf20Sopenharmony_ci ppaf->pln_offset, ppaf->pln_len, 10268c2ecf20Sopenharmony_ci ppaf->blk_offset, ppaf->blk_len, 10278c2ecf20Sopenharmony_ci ppaf->pg_offset, ppaf->pg_len, 10288c2ecf20Sopenharmony_ci ppaf->sec_offset, ppaf->sec_len); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic ssize_t nvm_dev_attr_show_12(struct device *dev, 10328c2ecf20Sopenharmony_ci struct device_attribute *dattr, char *page) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvme_get_ns_from_dev(dev); 10358c2ecf20Sopenharmony_ci struct nvm_dev *ndev = ns->ndev; 10368c2ecf20Sopenharmony_ci struct nvm_geo *geo = &ndev->geo; 10378c2ecf20Sopenharmony_ci struct attribute *attr; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (!ndev) 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci attr = &dattr->attr; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (strcmp(attr->name, "vendor_opcode") == 0) { 10458c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->vmnt); 10468c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "device_mode") == 0) { 10478c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->dom); 10488c2ecf20Sopenharmony_ci /* kept for compatibility */ 10498c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "media_manager") == 0) { 10508c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%s\n", "gennvm"); 10518c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "ppa_format") == 0) { 10528c2ecf20Sopenharmony_ci return nvm_dev_attr_show_ppaf((void *)&geo->addrf, page); 10538c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "media_type") == 0) { /* u8 */ 10548c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->mtype); 10558c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "flash_media_type") == 0) { 10568c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->fmtype); 10578c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "num_channels") == 0) { 10588c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_ch); 10598c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "num_luns") == 0) { 10608c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_lun); 10618c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "num_planes") == 0) { 10628c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_pln); 10638c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "num_blocks") == 0) { /* u16 */ 10648c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_chk); 10658c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "num_pages") == 0) { 10668c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_pg); 10678c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "page_size") == 0) { 10688c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->fpg_sz); 10698c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "hw_sector_size") == 0) { 10708c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->csecs); 10718c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */ 10728c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->sos); 10738c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "prog_typ") == 0) { 10748c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprt); 10758c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "prog_max") == 0) { 10768c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprm); 10778c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "erase_typ") == 0) { 10788c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbet); 10798c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "erase_max") == 0) { 10808c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbem); 10818c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "multiplane_modes") == 0) { 10828c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "0x%08x\n", geo->mpos); 10838c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "media_capabilities") == 0) { 10848c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "0x%08x\n", geo->mccap); 10858c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "max_phys_secs") == 0) { 10868c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", NVM_MAX_VLBA); 10878c2ecf20Sopenharmony_ci } else { 10888c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, 10898c2ecf20Sopenharmony_ci "Unhandled attr(%s) in `%s`\n", 10908c2ecf20Sopenharmony_ci attr->name, __func__); 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic ssize_t nvm_dev_attr_show_20(struct device *dev, 10958c2ecf20Sopenharmony_ci struct device_attribute *dattr, char *page) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct nvme_ns *ns = nvme_get_ns_from_dev(dev); 10988c2ecf20Sopenharmony_ci struct nvm_dev *ndev = ns->ndev; 10998c2ecf20Sopenharmony_ci struct nvm_geo *geo = &ndev->geo; 11008c2ecf20Sopenharmony_ci struct attribute *attr; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (!ndev) 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci attr = &dattr->attr; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (strcmp(attr->name, "groups") == 0) { 11088c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_ch); 11098c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "punits") == 0) { 11108c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_lun); 11118c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "chunks") == 0) { 11128c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_chk); 11138c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "clba") == 0) { 11148c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->clba); 11158c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "ws_min") == 0) { 11168c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->ws_min); 11178c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "ws_opt") == 0) { 11188c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->ws_opt); 11198c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "maxoc") == 0) { 11208c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->maxoc); 11218c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "maxocpu") == 0) { 11228c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->maxocpu); 11238c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "mw_cunits") == 0) { 11248c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->mw_cunits); 11258c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "write_typ") == 0) { 11268c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprt); 11278c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "write_max") == 0) { 11288c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprm); 11298c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "reset_typ") == 0) { 11308c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbet); 11318c2ecf20Sopenharmony_ci } else if (strcmp(attr->name, "reset_max") == 0) { 11328c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbem); 11338c2ecf20Sopenharmony_ci } else { 11348c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, 11358c2ecf20Sopenharmony_ci "Unhandled attr(%s) in `%s`\n", 11368c2ecf20Sopenharmony_ci attr->name, __func__); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci#define NVM_DEV_ATTR_RO(_name) \ 11418c2ecf20Sopenharmony_ci DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL) 11428c2ecf20Sopenharmony_ci#define NVM_DEV_ATTR_12_RO(_name) \ 11438c2ecf20Sopenharmony_ci DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_12, NULL) 11448c2ecf20Sopenharmony_ci#define NVM_DEV_ATTR_20_RO(_name) \ 11458c2ecf20Sopenharmony_ci DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_20, NULL) 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci/* general attributes */ 11488c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_RO(version); 11498c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_RO(capabilities); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_RO(read_typ); 11528c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_RO(read_max); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci/* 1.2 values */ 11558c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(vendor_opcode); 11568c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(device_mode); 11578c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(ppa_format); 11588c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(media_manager); 11598c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(media_type); 11608c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(flash_media_type); 11618c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(num_channels); 11628c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(num_luns); 11638c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(num_planes); 11648c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(num_blocks); 11658c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(num_pages); 11668c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(page_size); 11678c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(hw_sector_size); 11688c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(oob_sector_size); 11698c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(prog_typ); 11708c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(prog_max); 11718c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(erase_typ); 11728c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(erase_max); 11738c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(multiplane_modes); 11748c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(media_capabilities); 11758c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_12_RO(max_phys_secs); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/* 2.0 values */ 11788c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(groups); 11798c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(punits); 11808c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(chunks); 11818c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(clba); 11828c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(ws_min); 11838c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(ws_opt); 11848c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(maxoc); 11858c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(maxocpu); 11868c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(mw_cunits); 11878c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(write_typ); 11888c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(write_max); 11898c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(reset_typ); 11908c2ecf20Sopenharmony_cistatic NVM_DEV_ATTR_20_RO(reset_max); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic struct attribute *nvm_dev_attrs[] = { 11938c2ecf20Sopenharmony_ci /* version agnostic attrs */ 11948c2ecf20Sopenharmony_ci &dev_attr_version.attr, 11958c2ecf20Sopenharmony_ci &dev_attr_capabilities.attr, 11968c2ecf20Sopenharmony_ci &dev_attr_read_typ.attr, 11978c2ecf20Sopenharmony_ci &dev_attr_read_max.attr, 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* 1.2 attrs */ 12008c2ecf20Sopenharmony_ci &dev_attr_vendor_opcode.attr, 12018c2ecf20Sopenharmony_ci &dev_attr_device_mode.attr, 12028c2ecf20Sopenharmony_ci &dev_attr_media_manager.attr, 12038c2ecf20Sopenharmony_ci &dev_attr_ppa_format.attr, 12048c2ecf20Sopenharmony_ci &dev_attr_media_type.attr, 12058c2ecf20Sopenharmony_ci &dev_attr_flash_media_type.attr, 12068c2ecf20Sopenharmony_ci &dev_attr_num_channels.attr, 12078c2ecf20Sopenharmony_ci &dev_attr_num_luns.attr, 12088c2ecf20Sopenharmony_ci &dev_attr_num_planes.attr, 12098c2ecf20Sopenharmony_ci &dev_attr_num_blocks.attr, 12108c2ecf20Sopenharmony_ci &dev_attr_num_pages.attr, 12118c2ecf20Sopenharmony_ci &dev_attr_page_size.attr, 12128c2ecf20Sopenharmony_ci &dev_attr_hw_sector_size.attr, 12138c2ecf20Sopenharmony_ci &dev_attr_oob_sector_size.attr, 12148c2ecf20Sopenharmony_ci &dev_attr_prog_typ.attr, 12158c2ecf20Sopenharmony_ci &dev_attr_prog_max.attr, 12168c2ecf20Sopenharmony_ci &dev_attr_erase_typ.attr, 12178c2ecf20Sopenharmony_ci &dev_attr_erase_max.attr, 12188c2ecf20Sopenharmony_ci &dev_attr_multiplane_modes.attr, 12198c2ecf20Sopenharmony_ci &dev_attr_media_capabilities.attr, 12208c2ecf20Sopenharmony_ci &dev_attr_max_phys_secs.attr, 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* 2.0 attrs */ 12238c2ecf20Sopenharmony_ci &dev_attr_groups.attr, 12248c2ecf20Sopenharmony_ci &dev_attr_punits.attr, 12258c2ecf20Sopenharmony_ci &dev_attr_chunks.attr, 12268c2ecf20Sopenharmony_ci &dev_attr_clba.attr, 12278c2ecf20Sopenharmony_ci &dev_attr_ws_min.attr, 12288c2ecf20Sopenharmony_ci &dev_attr_ws_opt.attr, 12298c2ecf20Sopenharmony_ci &dev_attr_maxoc.attr, 12308c2ecf20Sopenharmony_ci &dev_attr_maxocpu.attr, 12318c2ecf20Sopenharmony_ci &dev_attr_mw_cunits.attr, 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci &dev_attr_write_typ.attr, 12348c2ecf20Sopenharmony_ci &dev_attr_write_max.attr, 12358c2ecf20Sopenharmony_ci &dev_attr_reset_typ.attr, 12368c2ecf20Sopenharmony_ci &dev_attr_reset_max.attr, 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci NULL, 12398c2ecf20Sopenharmony_ci}; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic umode_t nvm_dev_attrs_visible(struct kobject *kobj, 12428c2ecf20Sopenharmony_ci struct attribute *attr, int index) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 12458c2ecf20Sopenharmony_ci struct gendisk *disk = dev_to_disk(dev); 12468c2ecf20Sopenharmony_ci struct nvme_ns *ns = disk->private_data; 12478c2ecf20Sopenharmony_ci struct nvm_dev *ndev = ns->ndev; 12488c2ecf20Sopenharmony_ci struct device_attribute *dev_attr = 12498c2ecf20Sopenharmony_ci container_of(attr, typeof(*dev_attr), attr); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (!ndev) 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (dev_attr->show == nvm_dev_attr_show) 12558c2ecf20Sopenharmony_ci return attr->mode; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci switch (ndev->geo.major_ver_id) { 12588c2ecf20Sopenharmony_ci case 1: 12598c2ecf20Sopenharmony_ci if (dev_attr->show == nvm_dev_attr_show_12) 12608c2ecf20Sopenharmony_ci return attr->mode; 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci case 2: 12638c2ecf20Sopenharmony_ci if (dev_attr->show == nvm_dev_attr_show_20) 12648c2ecf20Sopenharmony_ci return attr->mode; 12658c2ecf20Sopenharmony_ci break; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return 0; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ciconst struct attribute_group nvme_nvm_attr_group = { 12728c2ecf20Sopenharmony_ci .name = "lightnvm", 12738c2ecf20Sopenharmony_ci .attrs = nvm_dev_attrs, 12748c2ecf20Sopenharmony_ci .is_visible = nvm_dev_attrs_visible, 12758c2ecf20Sopenharmony_ci}; 1276