1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * sfr.c - driver for special function registers 4 * 5 * Copyright (C) 2019 Bootlin. 6 * 7 */ 8 #include <linux/mfd/syscon.h> 9 #include <linux/module.h> 10 #include <linux/nvmem-provider.h> 11 #include <linux/random.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/regmap.h> 15 16 #define SFR_SN0 0x4c 17 #define SFR_SN_SIZE 8 18 19 struct atmel_sfr_priv { 20 struct regmap *regmap; 21 }; 22 23 static int atmel_sfr_read(void *context, unsigned int offset, 24 void *buf, size_t bytes) 25 { 26 struct atmel_sfr_priv *priv = context; 27 28 return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, 29 buf, bytes / 4); 30 } 31 32 static struct nvmem_config atmel_sfr_nvmem_config = { 33 .name = "atmel-sfr", 34 .read_only = true, 35 .word_size = 4, 36 .stride = 4, 37 .size = SFR_SN_SIZE, 38 .reg_read = atmel_sfr_read, 39 }; 40 41 static int atmel_sfr_probe(struct platform_device *pdev) 42 { 43 struct device *dev = &pdev->dev; 44 struct device_node *np = dev->of_node; 45 struct nvmem_device *nvmem; 46 struct atmel_sfr_priv *priv; 47 u8 sn[SFR_SN_SIZE]; 48 int ret; 49 50 priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 51 if (!priv) 52 return -ENOMEM; 53 54 priv->regmap = syscon_node_to_regmap(np); 55 if (IS_ERR(priv->regmap)) { 56 dev_err(dev, "cannot get parent's regmap\n"); 57 return PTR_ERR(priv->regmap); 58 } 59 60 atmel_sfr_nvmem_config.dev = dev; 61 atmel_sfr_nvmem_config.priv = priv; 62 63 nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); 64 if (IS_ERR(nvmem)) { 65 dev_err(dev, "error registering nvmem config\n"); 66 return PTR_ERR(nvmem); 67 } 68 69 ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); 70 if (ret == 0) 71 add_device_randomness(sn, SFR_SN_SIZE); 72 73 return ret; 74 } 75 76 static const struct of_device_id atmel_sfr_dt_ids[] = { 77 { 78 .compatible = "atmel,sama5d2-sfr", 79 }, { 80 .compatible = "atmel,sama5d4-sfr", 81 }, { 82 /* sentinel */ 83 }, 84 }; 85 MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); 86 87 static struct platform_driver atmel_sfr_driver = { 88 .probe = atmel_sfr_probe, 89 .driver = { 90 .name = "atmel-sfr", 91 .of_match_table = atmel_sfr_dt_ids, 92 }, 93 }; 94 module_platform_driver(atmel_sfr_driver); 95 96 MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); 97 MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); 98 MODULE_LICENSE("GPL v2"); 99