1 /* 2 * i.MX6 OCOTP fusebox driver 3 * 4 * Copyright (c) 2015 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de> 5 * 6 * Based on the barebox ocotp driver, 7 * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>, 8 * Orex Computed Radiography 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 12 * as published by the Free Software Foundation. 13 * 14 * http://www.opensource.org/licenses/gpl-license.html 15 * http://www.gnu.org/copyleft/gpl.html 16 */ 17 18 #include <linux/clk.h> 19 #include <linux/device.h> 20 #include <linux/io.h> 21 #include <linux/module.h> 22 #include <linux/nvmem-provider.h> 23 #include <linux/of.h> 24 #include <linux/of_device.h> 25 #include <linux/platform_device.h> 26 #include <linux/slab.h> 27 28 struct ocotp_priv { 29 struct device *dev; 30 struct clk *clk; 31 void __iomem *base; 32 unsigned int nregs; 33 }; 34 35 static int imx_ocotp_read(void *context, unsigned int offset, 36 void *val, size_t bytes) 37 { 38 struct ocotp_priv *priv = context; 39 unsigned int count; 40 u32 *buf = val; 41 int i, ret; 42 u32 index; 43 44 index = offset >> 2; 45 count = bytes >> 2; 46 47 if (count > (priv->nregs - index)) 48 count = priv->nregs - index; 49 50 ret = clk_prepare_enable(priv->clk); 51 if (ret < 0) { 52 dev_err(priv->dev, "failed to prepare/enable ocotp clk\n"); 53 return ret; 54 } 55 for (i = index; i < (index + count); i++) 56 *buf++ = readl(priv->base + 0x400 + i * 0x10); 57 58 clk_disable_unprepare(priv->clk); 59 60 return 0; 61 } 62 63 static struct nvmem_config imx_ocotp_nvmem_config = { 64 .name = "imx-ocotp", 65 .read_only = true, 66 .word_size = 4, 67 .stride = 4, 68 .owner = THIS_MODULE, 69 .reg_read = imx_ocotp_read, 70 }; 71 72 static const struct of_device_id imx_ocotp_dt_ids[] = { 73 { .compatible = "fsl,imx6q-ocotp", (void *)128 }, 74 { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, 75 { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, 76 { .compatible = "fsl,imx6ul-ocotp", (void *)128 }, 77 { }, 78 }; 79 MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); 80 81 static int imx_ocotp_probe(struct platform_device *pdev) 82 { 83 const struct of_device_id *of_id; 84 struct device *dev = &pdev->dev; 85 struct resource *res; 86 struct ocotp_priv *priv; 87 struct nvmem_device *nvmem; 88 89 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 90 if (!priv) 91 return -ENOMEM; 92 93 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 94 priv->base = devm_ioremap_resource(dev, res); 95 if (IS_ERR(priv->base)) 96 return PTR_ERR(priv->base); 97 98 priv->clk = devm_clk_get(&pdev->dev, NULL); 99 if (IS_ERR(priv->clk)) 100 return PTR_ERR(priv->clk); 101 102 of_id = of_match_device(imx_ocotp_dt_ids, dev); 103 priv->nregs = (unsigned long)of_id->data; 104 imx_ocotp_nvmem_config.size = 4 * priv->nregs; 105 imx_ocotp_nvmem_config.dev = dev; 106 imx_ocotp_nvmem_config.priv = priv; 107 nvmem = nvmem_register(&imx_ocotp_nvmem_config); 108 if (IS_ERR(nvmem)) 109 return PTR_ERR(nvmem); 110 111 platform_set_drvdata(pdev, nvmem); 112 113 return 0; 114 } 115 116 static int imx_ocotp_remove(struct platform_device *pdev) 117 { 118 struct nvmem_device *nvmem = platform_get_drvdata(pdev); 119 120 return nvmem_unregister(nvmem); 121 } 122 123 static struct platform_driver imx_ocotp_driver = { 124 .probe = imx_ocotp_probe, 125 .remove = imx_ocotp_remove, 126 .driver = { 127 .name = "imx_ocotp", 128 .of_match_table = imx_ocotp_dt_ids, 129 }, 130 }; 131 module_platform_driver(imx_ocotp_driver); 132 133 MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); 134 MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); 135 MODULE_LICENSE("GPL v2"); 136