blob: 0338c27ab95a927027dc0a125db5a9a981207be5 [file] [log] [blame]
Simon A. F. Lund40267ef2016-09-16 14:25:08 +02001#include <linux/kernel.h>
2#include <linux/lightnvm.h>
3#include <linux/miscdevice.h>
4#include <linux/kobject.h>
5#include <linux/blk-mq.h>
6
7#include "lightnvm.h"
8
9static ssize_t nvm_dev_attr_show(struct device *dev,
10 struct device_attribute *dattr, char *page)
11{
12 struct nvm_dev *ndev = container_of(dev, struct nvm_dev, dev);
13 struct nvm_id *id = &ndev->identity;
14 struct nvm_id_group *grp = &id->groups[0];
15 struct attribute *attr = &dattr->attr;
16
17 if (strcmp(attr->name, "version") == 0) {
18 return scnprintf(page, PAGE_SIZE, "%u\n", id->ver_id);
19 } else if (strcmp(attr->name, "vendor_opcode") == 0) {
20 return scnprintf(page, PAGE_SIZE, "%u\n", id->vmnt);
21 } else if (strcmp(attr->name, "capabilities") == 0) {
22 return scnprintf(page, PAGE_SIZE, "%u\n", id->cap);
23 } else if (strcmp(attr->name, "device_mode") == 0) {
24 return scnprintf(page, PAGE_SIZE, "%u\n", id->dom);
25 } else if (strcmp(attr->name, "media_manager") == 0) {
26 if (!ndev->mt)
27 return scnprintf(page, PAGE_SIZE, "%s\n", "none");
28 return scnprintf(page, PAGE_SIZE, "%s\n", ndev->mt->name);
29 } else if (strcmp(attr->name, "ppa_format") == 0) {
30 return scnprintf(page, PAGE_SIZE,
31 "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
32 id->ppaf.ch_offset, id->ppaf.ch_len,
33 id->ppaf.lun_offset, id->ppaf.lun_len,
34 id->ppaf.pln_offset, id->ppaf.pln_len,
35 id->ppaf.blk_offset, id->ppaf.blk_len,
36 id->ppaf.pg_offset, id->ppaf.pg_len,
37 id->ppaf.sect_offset, id->ppaf.sect_len);
38 } else if (strcmp(attr->name, "media_type") == 0) { /* u8 */
39 return scnprintf(page, PAGE_SIZE, "%u\n", grp->mtype);
40 } else if (strcmp(attr->name, "flash_media_type") == 0) {
41 return scnprintf(page, PAGE_SIZE, "%u\n", grp->fmtype);
42 } else if (strcmp(attr->name, "num_channels") == 0) {
43 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_ch);
44 } else if (strcmp(attr->name, "num_luns") == 0) {
45 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_lun);
46 } else if (strcmp(attr->name, "num_planes") == 0) {
47 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pln);
48 } else if (strcmp(attr->name, "num_blocks") == 0) { /* u16 */
49 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_blk);
50 } else if (strcmp(attr->name, "num_pages") == 0) {
51 return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pg);
52 } else if (strcmp(attr->name, "page_size") == 0) {
53 return scnprintf(page, PAGE_SIZE, "%u\n", grp->fpg_sz);
54 } else if (strcmp(attr->name, "hw_sector_size") == 0) {
55 return scnprintf(page, PAGE_SIZE, "%u\n", grp->csecs);
56 } else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */
57 return scnprintf(page, PAGE_SIZE, "%u\n", grp->sos);
58 } else if (strcmp(attr->name, "read_typ") == 0) {
59 return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdt);
60 } else if (strcmp(attr->name, "read_max") == 0) {
61 return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdm);
62 } else if (strcmp(attr->name, "prog_typ") == 0) {
63 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprt);
64 } else if (strcmp(attr->name, "prog_max") == 0) {
65 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprm);
66 } else if (strcmp(attr->name, "erase_typ") == 0) {
67 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbet);
68 } else if (strcmp(attr->name, "erase_max") == 0) {
69 return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbem);
70 } else if (strcmp(attr->name, "multiplane_modes") == 0) {
71 return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mpos);
72 } else if (strcmp(attr->name, "media_capabilities") == 0) {
73 return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mccap);
74 } else if (strcmp(attr->name, "max_phys_secs") == 0) {
75 return scnprintf(page, PAGE_SIZE, "%u\n",
76 ndev->ops->max_phys_sect);
77 } else {
78 return scnprintf(page,
79 PAGE_SIZE,
80 "Unhandled attr(%s) in `nvm_dev_attr_show`\n",
81 attr->name);
82 }
83}
84
85#define NVM_DEV_ATTR_RO(_name) \
86 DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL)
87
88static NVM_DEV_ATTR_RO(version);
89static NVM_DEV_ATTR_RO(vendor_opcode);
90static NVM_DEV_ATTR_RO(capabilities);
91static NVM_DEV_ATTR_RO(device_mode);
92static NVM_DEV_ATTR_RO(ppa_format);
93static NVM_DEV_ATTR_RO(media_manager);
94
95static NVM_DEV_ATTR_RO(media_type);
96static NVM_DEV_ATTR_RO(flash_media_type);
97static NVM_DEV_ATTR_RO(num_channels);
98static NVM_DEV_ATTR_RO(num_luns);
99static NVM_DEV_ATTR_RO(num_planes);
100static NVM_DEV_ATTR_RO(num_blocks);
101static NVM_DEV_ATTR_RO(num_pages);
102static NVM_DEV_ATTR_RO(page_size);
103static NVM_DEV_ATTR_RO(hw_sector_size);
104static NVM_DEV_ATTR_RO(oob_sector_size);
105static NVM_DEV_ATTR_RO(read_typ);
106static NVM_DEV_ATTR_RO(read_max);
107static NVM_DEV_ATTR_RO(prog_typ);
108static NVM_DEV_ATTR_RO(prog_max);
109static NVM_DEV_ATTR_RO(erase_typ);
110static NVM_DEV_ATTR_RO(erase_max);
111static NVM_DEV_ATTR_RO(multiplane_modes);
112static NVM_DEV_ATTR_RO(media_capabilities);
113static NVM_DEV_ATTR_RO(max_phys_secs);
114
115#define NVM_DEV_ATTR(_name) (dev_attr_##_name##)
116
117static struct attribute *nvm_dev_attrs[] = {
118 &dev_attr_version.attr,
119 &dev_attr_vendor_opcode.attr,
120 &dev_attr_capabilities.attr,
121 &dev_attr_device_mode.attr,
122 &dev_attr_media_manager.attr,
123
124 &dev_attr_ppa_format.attr,
125 &dev_attr_media_type.attr,
126 &dev_attr_flash_media_type.attr,
127 &dev_attr_num_channels.attr,
128 &dev_attr_num_luns.attr,
129 &dev_attr_num_planes.attr,
130 &dev_attr_num_blocks.attr,
131 &dev_attr_num_pages.attr,
132 &dev_attr_page_size.attr,
133 &dev_attr_hw_sector_size.attr,
134 &dev_attr_oob_sector_size.attr,
135 &dev_attr_read_typ.attr,
136 &dev_attr_read_max.attr,
137 &dev_attr_prog_typ.attr,
138 &dev_attr_prog_max.attr,
139 &dev_attr_erase_typ.attr,
140 &dev_attr_erase_max.attr,
141 &dev_attr_multiplane_modes.attr,
142 &dev_attr_media_capabilities.attr,
143 &dev_attr_max_phys_secs.attr,
144 NULL,
145};
146
147static struct attribute_group nvm_dev_attr_group = {
148 .name = "lightnvm",
149 .attrs = nvm_dev_attrs,
150};
151
152static const struct attribute_group *nvm_dev_attr_groups[] = {
153 &nvm_dev_attr_group,
154 NULL,
155};
156
157static void nvm_dev_release(struct device *device)
158{
159 struct nvm_dev *dev = container_of(device, struct nvm_dev, dev);
160 struct request_queue *q = dev->q;
161
162 pr_debug("nvm/sysfs: `nvm_dev_release`\n");
163
164 blk_mq_unregister_dev(device, q);
165
166 nvm_free(dev);
167}
168
169static struct device_type nvm_type = {
170 .name = "lightnvm",
171 .groups = nvm_dev_attr_groups,
172 .release = nvm_dev_release,
173};
174
175int nvm_sysfs_register_dev(struct nvm_dev *dev)
176{
Arnd Bergmann1e3aeae2016-09-16 14:25:09 +0200177 int ret;
178
Simon A. F. Lund40267ef2016-09-16 14:25:08 +0200179 if (!dev->parent_dev)
180 return 0;
181
182 dev->dev.parent = dev->parent_dev;
183 dev_set_name(&dev->dev, "%s", dev->name);
184 dev->dev.type = &nvm_type;
185 device_initialize(&dev->dev);
Arnd Bergmann1e3aeae2016-09-16 14:25:09 +0200186 ret = device_add(&dev->dev);
Simon A. F. Lund40267ef2016-09-16 14:25:08 +0200187
Arnd Bergmann1e3aeae2016-09-16 14:25:09 +0200188 if (!ret)
189 blk_mq_register_dev(&dev->dev, dev->q);
Simon A. F. Lund40267ef2016-09-16 14:25:08 +0200190
Arnd Bergmann1e3aeae2016-09-16 14:25:09 +0200191 return ret;
Simon A. F. Lund40267ef2016-09-16 14:25:08 +0200192}
193
194void nvm_sysfs_unregister_dev(struct nvm_dev *dev)
195{
196 if (dev && dev->parent_dev)
197 kobject_put(&dev->dev.kobj);
198}