summaryrefslogtreecommitdiffstats
path: root/drivers/nvmem/kvx-otp-nv.c
blob: d614c16e1efe42dbced0ec3c34ffff2b469cfce5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2020 Kalray Inc., Clément Léger
 */

#include <common.h>
#include <driver.h>
#include <malloc.h>
#include <xfuncs.h>
#include <errno.h>
#include <init.h>
#include <net.h>
#include <io.h>

#include <linux/nvmem-provider.h>

#define OTP_NV_ALIGN		4
#define OTP_NV_ALIGN_MASK	(OTP_NV_ALIGN - 1)

struct kvx_otp_nv_priv {
	void __iomem *base;
};

static int kvx_otp_nv_read(void *context, unsigned int offset,
			  void *_val, size_t bytes)
{
	struct kvx_otp_nv_priv *priv = context;
	u8 *val = _val;
	u32 tmp, copy_size;
	u8 skip = offset & OTP_NV_ALIGN_MASK;

	offset &= ~OTP_NV_ALIGN_MASK;

	while (bytes) {
		tmp = readl(priv->base + offset);
		if (skip != 0)
			copy_size = min(OTP_NV_ALIGN - skip, (int) bytes);
		else
			copy_size = min(bytes, sizeof(tmp));

		memcpy(val, ((u8 *) &tmp) + skip, copy_size);
		skip = 0;

		bytes -= copy_size;
		val += copy_size;
		offset += OTP_NV_ALIGN;
	}

	return 0;
}

static const struct nvmem_bus kvx_otp_nv_bus = {
	.read = kvx_otp_nv_read,
};

static const struct of_device_id kvx_otp_nv_match[] = {
	{ .compatible = "kalray,kvx-otp-nv" },
	{ /* sentinel */},
};

static int kvx_otp_nv_probe(struct device_d *dev)
{
	struct resource *res;
	struct nvmem_device *nvmem;
	struct nvmem_config econfig = { 0 };
	struct kvx_otp_nv_priv *priv;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	res = dev_request_mem_resource(dev, 0);
	if (IS_ERR(res))
		return PTR_ERR(res);

	priv->base = IOMEM(res->start);

	econfig.name = "kvx-nv-regbank";
	econfig.stride = 1;
	econfig.word_size = 1;
	econfig.size = resource_size(res);
	econfig.dev = dev;
	econfig.priv = priv;
	econfig.bus = &kvx_otp_nv_bus;

	dev->priv = priv;

	nvmem = nvmem_register(&econfig);

	return PTR_ERR_OR_ZERO(nvmem);
}

static struct driver_d kvx_otp_nv_driver = {
	.name	= "kvx-otp-nv",
	.probe	= kvx_otp_nv_probe,
	.of_compatible = DRV_OF_COMPAT(kvx_otp_nv_match),
};
postcore_platform_driver(kvx_otp_nv_driver);