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/device.h> 19 #include <linux/io.h> 20 #include <linux/module.h> 21 #include <linux/nvmem-provider.h> 22 #include <linux/of.h> 23 #include <linux/of_device.h> 24 #include <linux/platform_device.h> 25 #include <linux/slab.h> 26 27 struct ocotp_priv { 28 struct device *dev; 29 void __iomem *base; 30 unsigned int nregs; 31 }; 32 33 static int imx_ocotp_read(void *context, unsigned int offset, 34 void *val, size_t bytes) 35 { 36 struct ocotp_priv *priv = context; 37 unsigned int count; 38 u32 *buf = val; 39 int i; 40 u32 index; 41 42 index = offset >> 2; 43 count = bytes >> 2; 44 45 if (count > (priv->nregs - index)) 46 count = priv->nregs - index; 47 48 for (i = index; i < (index + count); i++) 49 *buf++ = readl(priv->base + 0x400 + i * 0x10); 50 51 return 0; 52 } 53 54 static struct nvmem_config imx_ocotp_nvmem_config = { 55 .name = "imx-ocotp", 56 .read_only = true, 57 .word_size = 4, 58 .stride = 4, 59 .owner = THIS_MODULE, 60 .reg_read = imx_ocotp_read, 61 }; 62 63 static const struct of_device_id imx_ocotp_dt_ids[] = { 64 { .compatible = "fsl,imx6q-ocotp", (void *)128 }, 65 { .compatible = "fsl,imx6sl-ocotp", (void *)32 }, 66 { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, 67 { }, 68 }; 69 MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); 70 71 static int imx_ocotp_probe(struct platform_device *pdev) 72 { 73 const struct of_device_id *of_id; 74 struct device *dev = &pdev->dev; 75 struct resource *res; 76 struct ocotp_priv *priv; 77 struct nvmem_device *nvmem; 78 79 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 80 if (!priv) 81 return -ENOMEM; 82 83 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 84 priv->base = devm_ioremap_resource(dev, res); 85 if (IS_ERR(priv->base)) 86 return PTR_ERR(priv->base); 87 88 of_id = of_match_device(imx_ocotp_dt_ids, dev); 89 priv->nregs = (unsigned int)of_id->data; 90 imx_ocotp_nvmem_config.size = 4 * priv->nregs; 91 imx_ocotp_nvmem_config.dev = dev; 92 imx_ocotp_nvmem_config.priv = priv; 93 nvmem = nvmem_register(&imx_ocotp_nvmem_config); 94 if (IS_ERR(nvmem)) 95 return PTR_ERR(nvmem); 96 97 platform_set_drvdata(pdev, nvmem); 98 99 return 0; 100 } 101 102 static int imx_ocotp_remove(struct platform_device *pdev) 103 { 104 struct nvmem_device *nvmem = platform_get_drvdata(pdev); 105 106 return nvmem_unregister(nvmem); 107 } 108 109 static struct platform_driver imx_ocotp_driver = { 110 .probe = imx_ocotp_probe, 111 .remove = imx_ocotp_remove, 112 .driver = { 113 .name = "imx_ocotp", 114 .of_match_table = imx_ocotp_dt_ids, 115 }, 116 }; 117 module_platform_driver(imx_ocotp_driver); 118 119 MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); 120 MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); 121 MODULE_LICENSE("GPL v2"); 122