18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * sfr.c - driver for special function registers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Bootlin. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/random.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define SFR_SN0 0x4c 188c2ecf20Sopenharmony_ci#define SFR_SN_SIZE 8 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct atmel_sfr_priv { 218c2ecf20Sopenharmony_ci struct regmap *regmap; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int atmel_sfr_read(void *context, unsigned int offset, 258c2ecf20Sopenharmony_ci void *buf, size_t bytes) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct atmel_sfr_priv *priv = context; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, 308c2ecf20Sopenharmony_ci buf, bytes / 4); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct nvmem_config atmel_sfr_nvmem_config = { 348c2ecf20Sopenharmony_ci .name = "atmel-sfr", 358c2ecf20Sopenharmony_ci .read_only = true, 368c2ecf20Sopenharmony_ci .word_size = 4, 378c2ecf20Sopenharmony_ci .stride = 4, 388c2ecf20Sopenharmony_ci .size = SFR_SN_SIZE, 398c2ecf20Sopenharmony_ci .reg_read = atmel_sfr_read, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int atmel_sfr_probe(struct platform_device *pdev) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 458c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 468c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 478c2ecf20Sopenharmony_ci struct atmel_sfr_priv *priv; 488c2ecf20Sopenharmony_ci u8 sn[SFR_SN_SIZE]; 498c2ecf20Sopenharmony_ci int ret; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!priv) 538c2ecf20Sopenharmony_ci return -ENOMEM; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci priv->regmap = syscon_node_to_regmap(np); 568c2ecf20Sopenharmony_ci if (IS_ERR(priv->regmap)) { 578c2ecf20Sopenharmony_ci dev_err(dev, "cannot get parent's regmap\n"); 588c2ecf20Sopenharmony_ci return PTR_ERR(priv->regmap); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci atmel_sfr_nvmem_config.dev = dev; 628c2ecf20Sopenharmony_ci atmel_sfr_nvmem_config.priv = priv; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); 658c2ecf20Sopenharmony_ci if (IS_ERR(nvmem)) { 668c2ecf20Sopenharmony_ci dev_err(dev, "error registering nvmem config\n"); 678c2ecf20Sopenharmony_ci return PTR_ERR(nvmem); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); 718c2ecf20Sopenharmony_ci if (ret == 0) 728c2ecf20Sopenharmony_ci add_device_randomness(sn, SFR_SN_SIZE); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_sfr_dt_ids[] = { 788c2ecf20Sopenharmony_ci { 798c2ecf20Sopenharmony_ci .compatible = "atmel,sama5d2-sfr", 808c2ecf20Sopenharmony_ci }, { 818c2ecf20Sopenharmony_ci .compatible = "atmel,sama5d4-sfr", 828c2ecf20Sopenharmony_ci }, { 838c2ecf20Sopenharmony_ci /* sentinel */ 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct platform_driver atmel_sfr_driver = { 898c2ecf20Sopenharmony_ci .probe = atmel_sfr_probe, 908c2ecf20Sopenharmony_ci .driver = { 918c2ecf20Sopenharmony_ci .name = "atmel-sfr", 928c2ecf20Sopenharmony_ci .of_match_table = atmel_sfr_dt_ids, 938c2ecf20Sopenharmony_ci }, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_cimodule_platform_driver(atmel_sfr_driver); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); 988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); 998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 100