162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/mtd/spi-nor.h>
462306a36Sopenharmony_ci#include <linux/spi/spi.h>
562306a36Sopenharmony_ci#include <linux/spi/spi-mem.h>
662306a36Sopenharmony_ci#include <linux/sysfs.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "core.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic ssize_t manufacturer_show(struct device *dev,
1162306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
1462306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
1562306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", nor->manufacturer->name);
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(manufacturer);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic ssize_t partname_show(struct device *dev,
2262306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
2562306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
2662306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", nor->info->name);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(partname);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic ssize_t jedec_id_show(struct device *dev,
3362306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
3662306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
3762306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
3862306a36Sopenharmony_ci	const u8 *id = nor->info->id_len ? nor->info->id : nor->id;
3962306a36Sopenharmony_ci	u8 id_len = nor->info->id_len ?: SPI_NOR_MAX_ID_LEN;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return sysfs_emit(buf, "%*phN\n", id_len, id);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(jedec_id);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct attribute *spi_nor_sysfs_entries[] = {
4662306a36Sopenharmony_ci	&dev_attr_manufacturer.attr,
4762306a36Sopenharmony_ci	&dev_attr_partname.attr,
4862306a36Sopenharmony_ci	&dev_attr_jedec_id.attr,
4962306a36Sopenharmony_ci	NULL
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic ssize_t sfdp_read(struct file *filp, struct kobject *kobj,
5362306a36Sopenharmony_ci			 struct bin_attribute *bin_attr, char *buf,
5462306a36Sopenharmony_ci			 loff_t off, size_t count)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
5762306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
5862306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
5962306a36Sopenharmony_ci	struct sfdp *sfdp = nor->sfdp;
6062306a36Sopenharmony_ci	size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return memory_read_from_buffer(buf, count, &off, nor->sfdp->dwords,
6362306a36Sopenharmony_ci				       sfdp_size);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_cistatic BIN_ATTR_RO(sfdp, 0);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct bin_attribute *spi_nor_sysfs_bin_entries[] = {
6862306a36Sopenharmony_ci	&bin_attr_sfdp,
6962306a36Sopenharmony_ci	NULL
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
7362306a36Sopenharmony_ci					struct attribute *attr, int n)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
7662306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
7762306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
8062306a36Sopenharmony_ci		return 0;
8162306a36Sopenharmony_ci	if (attr == &dev_attr_jedec_id.attr && !nor->info->id_len && !nor->id)
8262306a36Sopenharmony_ci		return 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0444;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
8862306a36Sopenharmony_ci					    struct bin_attribute *attr, int n)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
9162306a36Sopenharmony_ci	struct spi_mem *spimem = spi_get_drvdata(spi);
9262306a36Sopenharmony_ci	struct spi_nor *nor = spi_mem_get_drvdata(spimem);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (attr == &bin_attr_sfdp && nor->sfdp)
9562306a36Sopenharmony_ci		return 0444;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic const struct attribute_group spi_nor_sysfs_group = {
10162306a36Sopenharmony_ci	.name		= "spi-nor",
10262306a36Sopenharmony_ci	.is_visible	= spi_nor_sysfs_is_visible,
10362306a36Sopenharmony_ci	.is_bin_visible	= spi_nor_sysfs_is_bin_visible,
10462306a36Sopenharmony_ci	.attrs		= spi_nor_sysfs_entries,
10562306a36Sopenharmony_ci	.bin_attrs	= spi_nor_sysfs_bin_entries,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciconst struct attribute_group *spi_nor_sysfs_groups[] = {
10962306a36Sopenharmony_ci	&spi_nor_sysfs_group,
11062306a36Sopenharmony_ci	NULL
11162306a36Sopenharmony_ci};
112