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 *)32 }, 75 { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, 76 { }, 77 }; 78 MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); 79 80 static int imx_ocotp_probe(struct platform_device *pdev) 81 { 82 const struct of_device_id *of_id; 83 struct device *dev = &pdev->dev; 84 struct resource *res; 85 struct ocotp_priv *priv; 86 struct nvmem_device *nvmem; 87 88 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 89 if (!priv) 90 return -ENOMEM; 91 92 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 93 priv->base = devm_ioremap_resource(dev, res); 94 if (IS_ERR(priv->base)) 95 return PTR_ERR(priv->base); 96 97 priv->clk = devm_clk_get(&pdev->dev, NULL); 98 if (IS_ERR(priv->clk)) 99 return PTR_ERR(priv->clk); 100 101 of_id = of_match_device(imx_ocotp_dt_ids, dev); 102 priv->nregs = (unsigned long)of_id->data; 103 imx_ocotp_nvmem_config.size = 4 * priv->nregs; 104 imx_ocotp_nvmem_config.dev = dev; 105 imx_ocotp_nvmem_config.priv = priv; 106 nvmem = nvmem_register(&imx_ocotp_nvmem_config); 107 if (IS_ERR(nvmem)) 108 return PTR_ERR(nvmem); 109 110 platform_set_drvdata(pdev, nvmem); 111 112 return 0; 113 } 114 115 static int imx_ocotp_remove(struct platform_device *pdev) 116 { 117 struct nvmem_device *nvmem = platform_get_drvdata(pdev); 118 119 return nvmem_unregister(nvmem); 120 } 121 122 static struct platform_driver imx_ocotp_driver = { 123 .probe = imx_ocotp_probe, 124 .remove = imx_ocotp_remove, 125 .driver = { 126 .name = "imx_ocotp", 127 .of_match_table = imx_ocotp_dt_ids, 128 }, 129 }; 130 module_platform_driver(imx_ocotp_driver); 131 132 MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); 133 MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); 134 MODULE_LICENSE("GPL v2"); 135