162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Layerscape SFP driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2022 Michael Walle <michael@walle.cc>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/nvmem-provider.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/property.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define LAYERSCAPE_SFP_OTP_OFFSET	0x0200
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct layerscape_sfp_priv {
2162306a36Sopenharmony_ci	struct regmap *regmap;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct layerscape_sfp_data {
2562306a36Sopenharmony_ci	int size;
2662306a36Sopenharmony_ci	enum regmap_endian endian;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int layerscape_sfp_read(void *context, unsigned int offset, void *val,
3062306a36Sopenharmony_ci			       size_t bytes)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct layerscape_sfp_priv *priv = context;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return regmap_bulk_read(priv->regmap,
3562306a36Sopenharmony_ci				LAYERSCAPE_SFP_OTP_OFFSET + offset, val,
3662306a36Sopenharmony_ci				bytes / 4);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic struct nvmem_config layerscape_sfp_nvmem_config = {
4062306a36Sopenharmony_ci	.name = "fsl-sfp",
4162306a36Sopenharmony_ci	.reg_read = layerscape_sfp_read,
4262306a36Sopenharmony_ci	.word_size = 4,
4362306a36Sopenharmony_ci	.stride = 4,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int layerscape_sfp_probe(struct platform_device *pdev)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	const struct layerscape_sfp_data *data;
4962306a36Sopenharmony_ci	struct layerscape_sfp_priv *priv;
5062306a36Sopenharmony_ci	struct nvmem_device *nvmem;
5162306a36Sopenharmony_ci	struct regmap_config config = { 0 };
5262306a36Sopenharmony_ci	void __iomem *base;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
5562306a36Sopenharmony_ci	if (!priv)
5662306a36Sopenharmony_ci		return -ENOMEM;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
5962306a36Sopenharmony_ci	if (IS_ERR(base))
6062306a36Sopenharmony_ci		return PTR_ERR(base);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	data = device_get_match_data(&pdev->dev);
6362306a36Sopenharmony_ci	config.reg_bits = 32;
6462306a36Sopenharmony_ci	config.reg_stride = 4;
6562306a36Sopenharmony_ci	config.val_bits = 32;
6662306a36Sopenharmony_ci	config.val_format_endian = data->endian;
6762306a36Sopenharmony_ci	config.max_register = LAYERSCAPE_SFP_OTP_OFFSET + data->size - 4;
6862306a36Sopenharmony_ci	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &config);
6962306a36Sopenharmony_ci	if (IS_ERR(priv->regmap))
7062306a36Sopenharmony_ci		return PTR_ERR(priv->regmap);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	layerscape_sfp_nvmem_config.size = data->size;
7362306a36Sopenharmony_ci	layerscape_sfp_nvmem_config.dev = &pdev->dev;
7462306a36Sopenharmony_ci	layerscape_sfp_nvmem_config.priv = priv;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	nvmem = devm_nvmem_register(&pdev->dev, &layerscape_sfp_nvmem_config);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(nvmem);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic const struct layerscape_sfp_data ls1021a_data = {
8262306a36Sopenharmony_ci	.size = 0x88,
8362306a36Sopenharmony_ci	.endian = REGMAP_ENDIAN_BIG,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct layerscape_sfp_data ls1028a_data = {
8762306a36Sopenharmony_ci	.size = 0x88,
8862306a36Sopenharmony_ci	.endian = REGMAP_ENDIAN_LITTLE,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct of_device_id layerscape_sfp_dt_ids[] = {
9262306a36Sopenharmony_ci	{ .compatible = "fsl,ls1021a-sfp", .data = &ls1021a_data },
9362306a36Sopenharmony_ci	{ .compatible = "fsl,ls1028a-sfp", .data = &ls1028a_data },
9462306a36Sopenharmony_ci	{},
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, layerscape_sfp_dt_ids);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct platform_driver layerscape_sfp_driver = {
9962306a36Sopenharmony_ci	.probe	= layerscape_sfp_probe,
10062306a36Sopenharmony_ci	.driver = {
10162306a36Sopenharmony_ci		.name	= "layerscape_sfp",
10262306a36Sopenharmony_ci		.of_match_table = layerscape_sfp_dt_ids,
10362306a36Sopenharmony_ci	},
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_cimodule_platform_driver(layerscape_sfp_driver);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciMODULE_AUTHOR("Michael Walle <michael@walle.cc>");
10862306a36Sopenharmony_ciMODULE_DESCRIPTION("Layerscape Security Fuse Processor driver");
10962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
110