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

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.comSigned-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 5db3fb40
...@@ -5,3 +5,14 @@ config AT91_SOC_ID ...@@ -5,3 +5,14 @@ config AT91_SOC_ID
default ARCH_AT91 default ARCH_AT91
help help
Include support for the SoC bus on the Atmel ARM SoCs. 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.
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_AT91_SOC_ID) += soc.o obj-$(CONFIG_AT91_SOC_ID) += soc.o
obj-$(CONFIG_AT91_SOC_SFR) += sfr.o
// 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment