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: Sven Peter <[email protected]>
---
v1 -> v2:
- fixed sparse warning about __iomem by introducing
struct apple_efuses_priv as done in other nvmem drivers
- make sure the driver actually works as a module by
setting .owner to THIS_MODULE and adding MODULE_DEVICE_TABLE
pointed out by Joey Gouly
MAINTAINERS | 1 +
drivers/nvmem/Kconfig | 12 ++++++
drivers/nvmem/Makefile | 2 +
drivers/nvmem/apple-efuses.c | 82 ++++++++++++++++++++++++++++++++++++
4 files changed, 97 insertions(+)
create mode 100644 drivers/nvmem/apple-efuses.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f70d8525cbd4..e3e973a3f651 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1781,6 +1781,7 @@ F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c
F: drivers/irqchip/irq-apple-aic.c
F: drivers/mailbox/apple-mailbox.c
+F: drivers/nvmem/apple-efuses.c
F: drivers/pinctrl/pinctrl-apple-gpio.c
F: drivers/soc/apple/*
F: drivers/watchdog/apple_wdt.c
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index da414617a54d..bba29d1aec96 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -300,4 +300,16 @@ config NVMEM_BRCM_NVRAM
This driver provides support for Broadcom's NVRAM that can be accessed
using I/O mapping.
+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
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index dcbbde35b6a8..1a8e54691d3e 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -61,3 +61,5 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
nvmem-rmem-y := rmem.o
obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
nvmem_brcm_nvram-y := brcm_nvram.o
+obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+nvmem-apple-efuses-y := apple-efuses.o
diff --git a/drivers/nvmem/apple-efuses.c b/drivers/nvmem/apple-efuses.c
new file mode 100644
index 000000000000..dd5576ec5408
--- /dev/null
+++ b/drivers/nvmem/apple-efuses.c
@@ -0,0 +1,82 @@
+// 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,
+ .owner = THIS_MODULE,
+ .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,
+ .owner = THIS_MODULE,
+ },
+ .probe = apple_efuses_probe,
+};
+
+module_platform_driver(apple_efuses_driver);
+
+MODULE_AUTHOR("Sven Peter <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.25.1
On 06/03/2022 13:18, Sven Peter wrote:
> 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: Sven Peter <[email protected]>
> ---
> v1 -> v2:
> - fixed sparse warning about __iomem by introducing
> struct apple_efuses_priv as done in other nvmem drivers
> - make sure the driver actually works as a module by
> setting .owner to THIS_MODULE and adding MODULE_DEVICE_TABLE
> pointed out by Joey Gouly
>
> MAINTAINERS | 1 +
> drivers/nvmem/Kconfig | 12 ++++++
> drivers/nvmem/Makefile | 2 +
> drivers/nvmem/apple-efuses.c | 82 ++++++++++++++++++++++++++++++++++++
> 4 files changed, 97 insertions(+)
> create mode 100644 drivers/nvmem/apple-efuses.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f70d8525cbd4..e3e973a3f651 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1781,6 +1781,7 @@ F: drivers/i2c/busses/i2c-pasemi-core.c
> F: drivers/i2c/busses/i2c-pasemi-platform.c
> F: drivers/irqchip/irq-apple-aic.c
> F: drivers/mailbox/apple-mailbox.c
> +F: drivers/nvmem/apple-efuses.c
> F: drivers/pinctrl/pinctrl-apple-gpio.c
> F: drivers/soc/apple/*
> F: drivers/watchdog/apple_wdt.c
> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> index da414617a54d..bba29d1aec96 100644
> --- a/drivers/nvmem/Kconfig
> +++ b/drivers/nvmem/Kconfig
> @@ -300,4 +300,16 @@ config NVMEM_BRCM_NVRAM
> This driver provides support for Broadcom's NVRAM that can be accessed
> using I/O mapping.
>
> +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
> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> index dcbbde35b6a8..1a8e54691d3e 100644
> --- a/drivers/nvmem/Makefile
> +++ b/drivers/nvmem/Makefile
> @@ -61,3 +61,5 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
> nvmem-rmem-y := rmem.o
> obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
> nvmem_brcm_nvram-y := brcm_nvram.o
> +obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
> +nvmem-apple-efuses-y := apple-efuses.o
> diff --git a/drivers/nvmem/apple-efuses.c b/drivers/nvmem/apple-efuses.c
> new file mode 100644
> index 000000000000..dd5576ec5408
> --- /dev/null
> +++ b/drivers/nvmem/apple-efuses.c
> @@ -0,0 +1,82 @@
> +// 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,
> + .owner = THIS_MODULE,
> + .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,
> + .owner = THIS_MODULE,
You wrote it also in changelog, but why do you need it? Doesn't core set
it? Coccinelle should complain about this as well.
Best regards,
Krzysztof
On Sun, Mar 6, 2022, at 13:25, Krzysztof Kozlowski wrote:
> On 06/03/2022 13:18, Sven Peter wrote:
>> 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: Sven Peter <[email protected]>
>> ---
>> v1 -> v2:
>> - fixed sparse warning about __iomem by introducing
>> struct apple_efuses_priv as done in other nvmem drivers
>> - make sure the driver actually works as a module by
>> setting .owner to THIS_MODULE and adding MODULE_DEVICE_TABLE
>> pointed out by Joey Gouly
>>
>> MAINTAINERS | 1 +
>> drivers/nvmem/Kconfig | 12 ++++++
>> drivers/nvmem/Makefile | 2 +
>> drivers/nvmem/apple-efuses.c | 82 ++++++++++++++++++++++++++++++++++++
>> 4 files changed, 97 insertions(+)
>> create mode 100644 drivers/nvmem/apple-efuses.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index f70d8525cbd4..e3e973a3f651 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -1781,6 +1781,7 @@ F: drivers/i2c/busses/i2c-pasemi-core.c
>> F: drivers/i2c/busses/i2c-pasemi-platform.c
>> F: drivers/irqchip/irq-apple-aic.c
>> F: drivers/mailbox/apple-mailbox.c
>> +F: drivers/nvmem/apple-efuses.c
>> F: drivers/pinctrl/pinctrl-apple-gpio.c
>> F: drivers/soc/apple/*
>> F: drivers/watchdog/apple_wdt.c
>> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
>> index da414617a54d..bba29d1aec96 100644
>> --- a/drivers/nvmem/Kconfig
>> +++ b/drivers/nvmem/Kconfig
>> @@ -300,4 +300,16 @@ config NVMEM_BRCM_NVRAM
>> This driver provides support for Broadcom's NVRAM that can be accessed
>> using I/O mapping.
>>
>> +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
>> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
>> index dcbbde35b6a8..1a8e54691d3e 100644
>> --- a/drivers/nvmem/Makefile
>> +++ b/drivers/nvmem/Makefile
>> @@ -61,3 +61,5 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
>> nvmem-rmem-y := rmem.o
>> obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
>> nvmem_brcm_nvram-y := brcm_nvram.o
>> +obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
>> +nvmem-apple-efuses-y := apple-efuses.o
>> diff --git a/drivers/nvmem/apple-efuses.c b/drivers/nvmem/apple-efuses.c
>> new file mode 100644
>> index 000000000000..dd5576ec5408
>> --- /dev/null
>> +++ b/drivers/nvmem/apple-efuses.c
>> @@ -0,0 +1,82 @@
>> +// 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,
>> + .owner = THIS_MODULE,
>> + .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,
>> + .owner = THIS_MODULE,
>
> You wrote it also in changelog, but why do you need it? Doesn't core set
> it? Coccinelle should complain about this as well.
>
yeah, you're right. I shouldn't have just blindly followed what Hector and Joey
had to do for another subsystem to make reference counting work there.
I just checked again and it's neither required here not in struct nvmem_config
because module_platform_driver -> platform_driver_register ->
__platform_driver_register will set it in struct platform_driver and the
nvmem core will take it from there and set it inside nvmem_config.
thanks,
Sven