brintos

brintos / linux-shallow public Read only

0
0
Text · 4.0 KiB · 7b78f18 Raw
196 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Freescale MXS On-Chip OTP driver4 *5 * Copyright (C) 2015 Stefan Wahren <stefan.wahren@i2se.com>6 *7 * Based on the driver from Huang Shijie and Christoph G. Baumann8 */9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/err.h>13#include <linux/io.h>14#include <linux/module.h>15#include <linux/nvmem-provider.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/property.h>19#include <linux/slab.h>20#include <linux/stmp_device.h>21 22/* OCOTP registers and bits */23 24#define BM_OCOTP_CTRL_RD_BANK_OPEN	BIT(12)25#define BM_OCOTP_CTRL_ERROR		BIT(9)26#define BM_OCOTP_CTRL_BUSY		BIT(8)27 28#define OCOTP_TIMEOUT		1000029#define OCOTP_DATA_OFFSET	0x2030 31struct mxs_ocotp {32	struct clk *clk;33	void __iomem *base;34	struct nvmem_device *nvmem;35};36 37static int mxs_ocotp_wait(struct mxs_ocotp *otp)38{39	int timeout = OCOTP_TIMEOUT;40	unsigned int status = 0;41 42	while (timeout--) {43		status = readl(otp->base);44 45		if (!(status & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)))46			break;47 48		cpu_relax();49	}50 51	if (status & BM_OCOTP_CTRL_BUSY)52		return -EBUSY;53	else if (status & BM_OCOTP_CTRL_ERROR)54		return -EIO;55 56	return 0;57}58 59static int mxs_ocotp_read(void *context, unsigned int offset,60			  void *val, size_t bytes)61{62	struct mxs_ocotp *otp = context;63	u32 *buf = val;64	int ret;65 66	ret = clk_enable(otp->clk);67	if (ret)68		return ret;69 70	writel(BM_OCOTP_CTRL_ERROR, otp->base + STMP_OFFSET_REG_CLR);71 72	ret = mxs_ocotp_wait(otp);73	if (ret)74		goto disable_clk;75 76	/* open OCOTP banks for read */77	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_SET);78 79	/* approximately wait 33 hclk cycles */80	udelay(1);81 82	ret = mxs_ocotp_wait(otp);83	if (ret)84		goto close_banks;85 86	while (bytes) {87		if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) {88			/* fill up non-data register */89			*buf++ = 0;90		} else {91			*buf++ = readl(otp->base + offset);92		}93 94		bytes -= 4;95		offset += 4;96	}97 98close_banks:99	/* close banks for power saving */100	writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_CLR);101 102disable_clk:103	clk_disable(otp->clk);104 105	return ret;106}107 108static struct nvmem_config ocotp_config = {109	.name = "mxs-ocotp",110	.stride = 16,111	.word_size = 4,112	.reg_read = mxs_ocotp_read,113};114 115struct mxs_data {116	int size;117};118 119static const struct mxs_data imx23_data = {120	.size = 0x220,121};122 123static const struct mxs_data imx28_data = {124	.size = 0x2a0,125};126 127static const struct of_device_id mxs_ocotp_match[] = {128	{ .compatible = "fsl,imx23-ocotp", .data = &imx23_data },129	{ .compatible = "fsl,imx28-ocotp", .data = &imx28_data },130	{ /* sentinel */},131};132MODULE_DEVICE_TABLE(of, mxs_ocotp_match);133 134static void mxs_ocotp_action(void *data)135{136	clk_unprepare(data);137}138 139static int mxs_ocotp_probe(struct platform_device *pdev)140{141	struct device *dev = &pdev->dev;142	const struct mxs_data *data;143	struct mxs_ocotp *otp;144	int ret;145 146	data = device_get_match_data(dev);147	if (!data)148		return -EINVAL;149 150	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);151	if (!otp)152		return -ENOMEM;153 154	otp->base = devm_platform_ioremap_resource(pdev, 0);155	if (IS_ERR(otp->base))156		return PTR_ERR(otp->base);157 158	otp->clk = devm_clk_get(&pdev->dev, NULL);159	if (IS_ERR(otp->clk))160		return PTR_ERR(otp->clk);161 162	ret = clk_prepare(otp->clk);163	if (ret < 0) {164		dev_err(dev, "failed to prepare clk: %d\n", ret);165		return ret;166	}167 168	ret = devm_add_action_or_reset(&pdev->dev, mxs_ocotp_action, otp->clk);169	if (ret)170		return ret;171 172	ocotp_config.size = data->size;173	ocotp_config.priv = otp;174	ocotp_config.dev = dev;175	otp->nvmem = devm_nvmem_register(dev, &ocotp_config);176	if (IS_ERR(otp->nvmem))177		return PTR_ERR(otp->nvmem);178 179	platform_set_drvdata(pdev, otp);180 181	return 0;182}183 184static struct platform_driver mxs_ocotp_driver = {185	.probe = mxs_ocotp_probe,186	.driver = {187		.name = "mxs-ocotp",188		.of_match_table = mxs_ocotp_match,189	},190};191 192module_platform_driver(mxs_ocotp_driver);193MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net");194MODULE_DESCRIPTION("driver for OCOTP in i.MX23/i.MX28");195MODULE_LICENSE("GPL v2");196