| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | #include <linux/mtd/spi-nor.h> |
| 4 | #include <linux/spi/spi.h> |
| 5 | #include <linux/spi/spi-mem.h> |
| 6 | #include <linux/sysfs.h> |
| 7 | |
| 8 | #include "core.h" |
| 9 | |
| 10 | static ssize_t manufacturer_show(struct device *dev, |
| 11 | struct device_attribute *attr, char *buf) |
| 12 | { |
| 13 | struct spi_device *spi = to_spi_device(dev); |
| 14 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 15 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 16 | |
| 17 | return sysfs_emit(buf, fmt: "%s\n" , nor->manufacturer->name); |
| 18 | } |
| 19 | static DEVICE_ATTR_RO(manufacturer); |
| 20 | |
| 21 | static ssize_t partname_show(struct device *dev, |
| 22 | struct device_attribute *attr, char *buf) |
| 23 | { |
| 24 | struct spi_device *spi = to_spi_device(dev); |
| 25 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 26 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 27 | |
| 28 | return sysfs_emit(buf, fmt: "%s\n" , nor->info->name); |
| 29 | } |
| 30 | static DEVICE_ATTR_RO(partname); |
| 31 | |
| 32 | static ssize_t jedec_id_show(struct device *dev, |
| 33 | struct device_attribute *attr, char *buf) |
| 34 | { |
| 35 | struct spi_device *spi = to_spi_device(dev); |
| 36 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 37 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 38 | const u8 *id = nor->info->id ? nor->info->id->bytes : nor->id; |
| 39 | u8 id_len = nor->info->id ? nor->info->id->len : SPI_NOR_MAX_ID_LEN; |
| 40 | |
| 41 | return sysfs_emit(buf, fmt: "%*phN\n" , id_len, id); |
| 42 | } |
| 43 | static DEVICE_ATTR_RO(jedec_id); |
| 44 | |
| 45 | static struct attribute *spi_nor_sysfs_entries[] = { |
| 46 | &dev_attr_manufacturer.attr, |
| 47 | &dev_attr_partname.attr, |
| 48 | &dev_attr_jedec_id.attr, |
| 49 | NULL |
| 50 | }; |
| 51 | |
| 52 | static ssize_t sfdp_read(struct file *filp, struct kobject *kobj, |
| 53 | const struct bin_attribute *bin_attr, char *buf, |
| 54 | loff_t off, size_t count) |
| 55 | { |
| 56 | struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); |
| 57 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 58 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 59 | struct sfdp *sfdp = nor->sfdp; |
| 60 | size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords); |
| 61 | |
| 62 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: nor->sfdp->dwords, |
| 63 | available: sfdp_size); |
| 64 | } |
| 65 | static const BIN_ATTR_RO(sfdp, 0); |
| 66 | |
| 67 | static const struct bin_attribute *const spi_nor_sysfs_bin_entries[] = { |
| 68 | &bin_attr_sfdp, |
| 69 | NULL |
| 70 | }; |
| 71 | |
| 72 | static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj, |
| 73 | struct attribute *attr, int n) |
| 74 | { |
| 75 | struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); |
| 76 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 77 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 78 | |
| 79 | if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer) |
| 80 | return 0; |
| 81 | if (attr == &dev_attr_partname.attr && !nor->info->name) |
| 82 | return 0; |
| 83 | if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id) |
| 84 | return 0; |
| 85 | |
| 86 | return 0444; |
| 87 | } |
| 88 | |
| 89 | static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj, |
| 90 | const struct bin_attribute *attr, int n) |
| 91 | { |
| 92 | struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); |
| 93 | struct spi_mem *spimem = spi_get_drvdata(spi); |
| 94 | struct spi_nor *nor = spi_mem_get_drvdata(mem: spimem); |
| 95 | |
| 96 | if (attr == &bin_attr_sfdp && nor->sfdp) |
| 97 | return 0444; |
| 98 | |
| 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | static const struct attribute_group spi_nor_sysfs_group = { |
| 103 | .name = "spi-nor" , |
| 104 | .is_visible = spi_nor_sysfs_is_visible, |
| 105 | .is_bin_visible = spi_nor_sysfs_is_bin_visible, |
| 106 | .attrs = spi_nor_sysfs_entries, |
| 107 | .bin_attrs_new = spi_nor_sysfs_bin_entries, |
| 108 | }; |
| 109 | |
| 110 | const struct attribute_group *spi_nor_sysfs_groups[] = { |
| 111 | &spi_nor_sysfs_group, |
| 112 | NULL |
| 113 | }; |
| 114 | |