xref: /openbmc/linux/drivers/mfd/wm831x-otp.c (revision 981ce06a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26704e517SMark Brown /*
36704e517SMark Brown  * wm831x-otp.c  --  OTP for Wolfson WM831x PMICs
46704e517SMark Brown  *
56704e517SMark Brown  * Copyright 2009 Wolfson Microelectronics PLC.
66704e517SMark Brown  *
76704e517SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
86704e517SMark Brown  */
96704e517SMark Brown 
106704e517SMark Brown #include <linux/kernel.h>
116704e517SMark Brown #include <linux/module.h>
126704e517SMark Brown #include <linux/i2c.h>
136704e517SMark Brown #include <linux/bcd.h>
146704e517SMark Brown #include <linux/delay.h>
156704e517SMark Brown #include <linux/mfd/core.h>
1627130f0cSMark Brown #include <linux/random.h>
176704e517SMark Brown 
186704e517SMark Brown #include <linux/mfd/wm831x/core.h>
196704e517SMark Brown #include <linux/mfd/wm831x/otp.h>
206704e517SMark Brown 
216704e517SMark Brown /* In bytes */
226704e517SMark Brown #define WM831X_UNIQUE_ID_LEN 16
236704e517SMark Brown 
246704e517SMark Brown /* Read the unique ID from the chip into id */
wm831x_unique_id_read(struct wm831x * wm831x,char * id)256704e517SMark Brown static int wm831x_unique_id_read(struct wm831x *wm831x, char *id)
266704e517SMark Brown {
276704e517SMark Brown 	int i, val;
286704e517SMark Brown 
296704e517SMark Brown 	for (i = 0; i < WM831X_UNIQUE_ID_LEN / 2; i++) {
306704e517SMark Brown 		val = wm831x_reg_read(wm831x, WM831X_UNIQUE_ID_1 + i);
316704e517SMark Brown 		if (val < 0)
326704e517SMark Brown 			return val;
336704e517SMark Brown 
346704e517SMark Brown 		id[i * 2]       = (val >> 8) & 0xff;
356704e517SMark Brown 		id[(i * 2) + 1] = val & 0xff;
366704e517SMark Brown 	}
376704e517SMark Brown 
386704e517SMark Brown 	return 0;
396704e517SMark Brown }
406704e517SMark Brown 
unique_id_show(struct device * dev,struct device_attribute * attr,char * buf)41*981ce06aSZhen Lei static ssize_t unique_id_show(struct device *dev,
426704e517SMark Brown 			      struct device_attribute *attr, char *buf)
436704e517SMark Brown {
446704e517SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(dev);
45a5656a70SRasmus Villemoes 	int rval;
466704e517SMark Brown 	char id[WM831X_UNIQUE_ID_LEN];
476704e517SMark Brown 
486704e517SMark Brown 	rval = wm831x_unique_id_read(wm831x, id);
496704e517SMark Brown 	if (rval < 0)
506704e517SMark Brown 		return 0;
516704e517SMark Brown 
52a5656a70SRasmus Villemoes 	return sprintf(buf, "%*phN\n", WM831X_UNIQUE_ID_LEN, id);
536704e517SMark Brown }
546704e517SMark Brown 
55*981ce06aSZhen Lei static DEVICE_ATTR_RO(unique_id);
566704e517SMark Brown 
wm831x_otp_init(struct wm831x * wm831x)576704e517SMark Brown int wm831x_otp_init(struct wm831x *wm831x)
586704e517SMark Brown {
5927130f0cSMark Brown 	char uuid[WM831X_UNIQUE_ID_LEN];
606704e517SMark Brown 	int ret;
616704e517SMark Brown 
626704e517SMark Brown 	ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
636704e517SMark Brown 	if (ret != 0)
646704e517SMark Brown 		dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
656704e517SMark Brown 			ret);
666704e517SMark Brown 
6727130f0cSMark Brown 	ret = wm831x_unique_id_read(wm831x, uuid);
6827130f0cSMark Brown 	if (ret == 0)
6927130f0cSMark Brown 		add_device_randomness(uuid, sizeof(uuid));
7027130f0cSMark Brown 	else
7127130f0cSMark Brown 		dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret);
7227130f0cSMark Brown 
736704e517SMark Brown 	return ret;
746704e517SMark Brown }
756704e517SMark Brown 
wm831x_otp_exit(struct wm831x * wm831x)766704e517SMark Brown void wm831x_otp_exit(struct wm831x *wm831x)
776704e517SMark Brown {
786704e517SMark Brown 	device_remove_file(wm831x->dev, &dev_attr_unique_id);
796704e517SMark Brown }
806704e517SMark Brown 
81