162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sfr.c - driver for special function registers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Bootlin. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/nvmem-provider.h> 1162306a36Sopenharmony_ci#include <linux/random.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define SFR_SN0 0x4c 1762306a36Sopenharmony_ci#define SFR_SN_SIZE 8 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct atmel_sfr_priv { 2062306a36Sopenharmony_ci struct regmap *regmap; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int atmel_sfr_read(void *context, unsigned int offset, 2462306a36Sopenharmony_ci void *buf, size_t bytes) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct atmel_sfr_priv *priv = context; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, 2962306a36Sopenharmony_ci buf, bytes / 4); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct nvmem_config atmel_sfr_nvmem_config = { 3362306a36Sopenharmony_ci .name = "atmel-sfr", 3462306a36Sopenharmony_ci .read_only = true, 3562306a36Sopenharmony_ci .word_size = 4, 3662306a36Sopenharmony_ci .stride = 4, 3762306a36Sopenharmony_ci .size = SFR_SN_SIZE, 3862306a36Sopenharmony_ci .reg_read = atmel_sfr_read, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int atmel_sfr_probe(struct platform_device *pdev) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 4462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 4562306a36Sopenharmony_ci struct nvmem_device *nvmem; 4662306a36Sopenharmony_ci struct atmel_sfr_priv *priv; 4762306a36Sopenharmony_ci u8 sn[SFR_SN_SIZE]; 4862306a36Sopenharmony_ci int ret; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 5162306a36Sopenharmony_ci if (!priv) 5262306a36Sopenharmony_ci return -ENOMEM; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci priv->regmap = syscon_node_to_regmap(np); 5562306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) { 5662306a36Sopenharmony_ci dev_err(dev, "cannot get parent's regmap\n"); 5762306a36Sopenharmony_ci return PTR_ERR(priv->regmap); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci atmel_sfr_nvmem_config.dev = dev; 6162306a36Sopenharmony_ci atmel_sfr_nvmem_config.priv = priv; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); 6462306a36Sopenharmony_ci if (IS_ERR(nvmem)) { 6562306a36Sopenharmony_ci dev_err(dev, "error registering nvmem config\n"); 6662306a36Sopenharmony_ci return PTR_ERR(nvmem); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); 7062306a36Sopenharmony_ci if (ret == 0) 7162306a36Sopenharmony_ci add_device_randomness(sn, SFR_SN_SIZE); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const struct of_device_id atmel_sfr_dt_ids[] = { 7762306a36Sopenharmony_ci { 7862306a36Sopenharmony_ci .compatible = "atmel,sama5d2-sfr", 7962306a36Sopenharmony_ci }, { 8062306a36Sopenharmony_ci .compatible = "atmel,sama5d4-sfr", 8162306a36Sopenharmony_ci }, { 8262306a36Sopenharmony_ci /* sentinel */ 8362306a36Sopenharmony_ci }, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct platform_driver atmel_sfr_driver = { 8862306a36Sopenharmony_ci .probe = atmel_sfr_probe, 8962306a36Sopenharmony_ci .driver = { 9062306a36Sopenharmony_ci .name = "atmel-sfr", 9162306a36Sopenharmony_ci .of_match_table = atmel_sfr_dt_ids, 9262306a36Sopenharmony_ci }, 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_cimodule_platform_driver(atmel_sfr_driver); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciMODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); 9762306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); 9862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 99