Commit b6b7ef93 authored by Sven Peter's avatar Sven Peter Committed by Greg Kroah-Hartman
Browse files

nvmem: Add Apple eFuse driver



Apple SoCs contain eFuses used to store factory-programmed data such
as calibration values for the PCIe or the Type-C PHY. They are organized
as 32bit values exposed as MMIO.

Signed-off-by: default avatarSven Peter <sven@svenpeter.dev>
Signed-off-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20220429162701.2222-6-srinivas.kandagatla@linaro.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7177042b
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -324,4 +324,16 @@ config NVMEM_SUNPLUS_OCOTP
	  This driver can also be built as a module. If so, the module
	  will be called nvmem-sunplus-ocotp.

config NVMEM_APPLE_EFUSES
	tristate "Apple eFuse support"
	depends on ARCH_APPLE || COMPILE_TEST
	default ARCH_APPLE
	help
	  Say y here to enable support for reading eFuses on Apple SoCs
	  such as the M1. These are e.g. used to store factory programmed
	  calibration data required for the PCIe or the USB-C PHY.

	  This driver can also be built as a module. If so, the module will
	  be called nvmem-apple-efuses.

endif
+2 −0
Original line number Diff line number Diff line
@@ -65,3 +65,5 @@ obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
nvmem-layerscape-sfp-y		:= layerscape-sfp.o
obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP)	+= nvmem_sunplus_ocotp.o
nvmem_sunplus_ocotp-y		:= sunplus-ocotp.o
obj-$(CONFIG_NVMEM_APPLE_EFUSES)	+= nvmem-apple-efuses.o
nvmem-apple-efuses-y 		:= apple-efuses.o
+80 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Apple SoC eFuse driver
 *
 * Copyright (C) The Asahi Linux Contributors
 */

#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/nvmem-provider.h>
#include <linux/platform_device.h>

struct apple_efuses_priv {
	void __iomem *fuses;
};

static int apple_efuses_read(void *context, unsigned int offset, void *val,
			     size_t bytes)
{
	struct apple_efuses_priv *priv = context;
	u32 *dst = val;

	while (bytes >= sizeof(u32)) {
		*dst++ = readl_relaxed(priv->fuses + offset);
		bytes -= sizeof(u32);
		offset += sizeof(u32);
	}

	return 0;
}

static int apple_efuses_probe(struct platform_device *pdev)
{
	struct apple_efuses_priv *priv;
	struct resource *res;
	struct nvmem_config config = {
		.dev = &pdev->dev,
		.read_only = true,
		.reg_read = apple_efuses_read,
		.stride = sizeof(u32),
		.word_size = sizeof(u32),
		.name = "apple_efuses_nvmem",
		.id = NVMEM_DEVID_AUTO,
		.root_only = true,
	};

	priv = devm_kzalloc(config.dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->fuses = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
	if (IS_ERR(priv->fuses))
		return PTR_ERR(priv->fuses);

	config.priv = priv;
	config.size = resource_size(res);

	return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config));
}

static const struct of_device_id apple_efuses_of_match[] = {
	{ .compatible = "apple,efuses", },
	{}
};

MODULE_DEVICE_TABLE(of, apple_efuses_of_match);

static struct platform_driver apple_efuses_driver = {
	.driver = {
		.name = "apple_efuses",
		.of_match_table = apple_efuses_of_match,
	},
	.probe = apple_efuses_probe,
};

module_platform_driver(apple_efuses_driver);

MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
MODULE_LICENSE("GPL");