Commit c3277f8e authored by Kamel Bouhara's avatar Kamel Bouhara Committed by Alexandre Belloni
Browse files

soc: at91: Add Atmel SFR SN (Serial Number) support



Add support to read SFR's read-only registers providing the SoC
Serial Numbers (SN0+SN1) to userspace.

~ #  hexdump -n 8  -e'"%d\n"' /sys/bus/nvmem/devices/atmel-sfr0/nvmem
959527243
371539274

Signed-off-by: default avatarKamel Bouhara <kamel.bouhara@bootlin.com>
Tested-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
Reviewed-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
Link: https://lore.kernel.org/r/20191004151802.21793-1-kamel.bouhara@bootlin.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 5db3fb40
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -5,3 +5,14 @@ config AT91_SOC_ID
	default ARCH_AT91
	help
	  Include support for the SoC bus on the Atmel ARM SoCs.

config AT91_SOC_SFR
	tristate "Special Function Registers support"
	depends on ARCH_AT91 || COMPILE_TEST
	help
	  This is a driver for the Special Function Registers available on
	  Atmel SAMA5Dx SoCs, providing access to specific aspects of the
	  integrated memory, bridge implementations, processor etc.

	  This driver can also be built as a module. If so, the module
	  will be called sfr.
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_AT91_SOC_ID) += soc.o
obj-$(CONFIG_AT91_SOC_SFR) += sfr.o
+99 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * sfr.c - driver for special function registers
 *
 * Copyright (C) 2019 Bootlin.
 *
 */
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/nvmem-provider.h>
#include <linux/random.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

#define SFR_SN0		0x4c
#define SFR_SN_SIZE	8

struct atmel_sfr_priv {
	struct regmap			*regmap;
};

static int atmel_sfr_read(void *context, unsigned int offset,
			  void *buf, size_t bytes)
{
	struct atmel_sfr_priv *priv = context;

	return regmap_bulk_read(priv->regmap, SFR_SN0 + offset,
				buf, bytes / 4);
}

static struct nvmem_config atmel_sfr_nvmem_config = {
	.name = "atmel-sfr",
	.read_only = true,
	.word_size = 4,
	.stride = 4,
	.size = SFR_SN_SIZE,
	.reg_read = atmel_sfr_read,
};

static int atmel_sfr_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	struct nvmem_device *nvmem;
	struct atmel_sfr_priv *priv;
	u8 sn[SFR_SN_SIZE];
	int ret;

	priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->regmap = syscon_node_to_regmap(np);
	if (IS_ERR(priv->regmap)) {
		dev_err(dev, "cannot get parent's regmap\n");
		return PTR_ERR(priv->regmap);
	}

	atmel_sfr_nvmem_config.dev = dev;
	atmel_sfr_nvmem_config.priv = priv;

	nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config);
	if (IS_ERR(nvmem)) {
		dev_err(dev, "error registering nvmem config\n");
		return PTR_ERR(nvmem);
	}

	ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE);
	if (ret == 0)
		add_device_randomness(sn, SFR_SN_SIZE);

	return ret;
}

static const struct of_device_id atmel_sfr_dt_ids[] = {
	{
		.compatible = "atmel,sama5d2-sfr",
	}, {
		.compatible = "atmel,sama5d4-sfr",
	}, {
		/* sentinel */
	},
};
MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids);

static struct platform_driver atmel_sfr_driver = {
	.probe = atmel_sfr_probe,
	.driver = {
		.name = "atmel-sfr",
		.of_match_table = atmel_sfr_dt_ids,
	},
};
module_platform_driver(atmel_sfr_driver);

MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family");
MODULE_LICENSE("GPL v2");