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