1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Apple SoC eFuse driver 4 * 5 * Copyright (C) The Asahi Linux Contributors 6 */ 7 8 #include <linux/io.h> 9 #include <linux/mod_devicetable.h> 10 #include <linux/module.h> 11 #include <linux/nvmem-provider.h> 12 #include <linux/platform_device.h> 13 14 struct apple_efuses_priv { 15 void __iomem *fuses; 16 }; 17 18 static int apple_efuses_read(void *context, unsigned int offset, void *val, 19 size_t bytes) 20 { 21 struct apple_efuses_priv *priv = context; 22 u32 *dst = val; 23 24 while (bytes >= sizeof(u32)) { 25 *dst++ = readl_relaxed(priv->fuses + offset); 26 bytes -= sizeof(u32); 27 offset += sizeof(u32); 28 } 29 30 return 0; 31 } 32 33 static int apple_efuses_probe(struct platform_device *pdev) 34 { 35 struct apple_efuses_priv *priv; 36 struct resource *res; 37 struct nvmem_config config = { 38 .dev = &pdev->dev, 39 .add_legacy_fixed_of_cells = true, 40 .read_only = true, 41 .reg_read = apple_efuses_read, 42 .stride = sizeof(u32), 43 .word_size = sizeof(u32), 44 .name = "apple_efuses_nvmem", 45 .id = NVMEM_DEVID_AUTO, 46 .root_only = true, 47 }; 48 49 priv = devm_kzalloc(config.dev, sizeof(*priv), GFP_KERNEL); 50 if (!priv) 51 return -ENOMEM; 52 53 priv->fuses = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 54 if (IS_ERR(priv->fuses)) 55 return PTR_ERR(priv->fuses); 56 57 config.priv = priv; 58 config.size = resource_size(res); 59 60 return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); 61 } 62 63 static const struct of_device_id apple_efuses_of_match[] = { 64 { .compatible = "apple,efuses", }, 65 {} 66 }; 67 68 MODULE_DEVICE_TABLE(of, apple_efuses_of_match); 69 70 static struct platform_driver apple_efuses_driver = { 71 .driver = { 72 .name = "apple_efuses", 73 .of_match_table = apple_efuses_of_match, 74 }, 75 .probe = apple_efuses_probe, 76 }; 77 78 module_platform_driver(apple_efuses_driver); 79 80 MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>"); 81 MODULE_DESCRIPTION("Apple SoC eFuse driver"); 82 MODULE_LICENSE("GPL"); 83