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/regmap.h> 26#include <linux/slab.h> 27 28struct ocotp_priv { 29 struct device *dev; 30 void __iomem *base; 31 unsigned int nregs; 32}; 33 34static int imx_ocotp_read(void *context, const void *reg, size_t reg_size, 35 void *val, size_t val_size) 36{ 37 struct ocotp_priv *priv = context; 38 unsigned int offset = *(u32 *)reg; 39 unsigned int count; 40 int i; 41 u32 index; 42 43 index = offset >> 2; 44 count = val_size >> 2; 45 46 if (count > (priv->nregs - index)) 47 count = priv->nregs - index; 48 49 for (i = index; i < (index + count); i++) { 50 *(u32 *)val = readl(priv->base + 0x400 + i * 0x10); 51 val += 4; 52 } 53 54 return (i - index) * 4; 55} 56 57static int imx_ocotp_write(void *context, const void *data, size_t count) 58{ 59 /* Not implemented */ 60 return 0; 61} 62 63static struct regmap_bus imx_ocotp_bus = { 64 .read = imx_ocotp_read, 65 .write = imx_ocotp_write, 66 .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 67 .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 68}; 69 70static bool imx_ocotp_writeable_reg(struct device *dev, unsigned int reg) 71{ 72 return false; 73} 74 75static struct regmap_config imx_ocotp_regmap_config = { 76 .reg_bits = 32, 77 .val_bits = 32, 78 .reg_stride = 4, 79 .writeable_reg = imx_ocotp_writeable_reg, 80 .name = "imx-ocotp", 81}; 82 83static struct nvmem_config imx_ocotp_nvmem_config = { 84 .name = "imx-ocotp", 85 .read_only = true, 86 .owner = THIS_MODULE, 87}; 88 89static const struct of_device_id imx_ocotp_dt_ids[] = { 90 { .compatible = "fsl,imx6q-ocotp", (void *)128 }, 91 { .compatible = "fsl,imx6sl-ocotp", (void *)32 }, 92 { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, 93 { }, 94}; 95MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); 96 97static int imx_ocotp_probe(struct platform_device *pdev) 98{ 99 const struct of_device_id *of_id; 100 struct device *dev = &pdev->dev; 101 struct resource *res; 102 struct regmap *regmap; 103 struct ocotp_priv *priv; 104 struct nvmem_device *nvmem; 105 106 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 107 if (!priv) 108 return -ENOMEM; 109 110 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 111 priv->base = devm_ioremap_resource(dev, res); 112 if (IS_ERR(priv->base)) 113 return PTR_ERR(priv->base); 114 115 of_id = of_match_device(imx_ocotp_dt_ids, dev); 116 priv->nregs = (unsigned int)of_id->data; 117 imx_ocotp_regmap_config.max_register = 4 * priv->nregs - 4; 118 119 regmap = devm_regmap_init(dev, &imx_ocotp_bus, priv, 120 &imx_ocotp_regmap_config); 121 if (IS_ERR(regmap)) { 122 dev_err(dev, "regmap init failed\n"); 123 return PTR_ERR(regmap); 124 } 125 imx_ocotp_nvmem_config.dev = dev; 126 nvmem = nvmem_register(&imx_ocotp_nvmem_config); 127 if (IS_ERR(nvmem)) 128 return PTR_ERR(nvmem); 129 130 platform_set_drvdata(pdev, nvmem); 131 132 return 0; 133} 134 135static int imx_ocotp_remove(struct platform_device *pdev) 136{ 137 struct nvmem_device *nvmem = platform_get_drvdata(pdev); 138 139 return nvmem_unregister(nvmem); 140} 141 142static struct platform_driver imx_ocotp_driver = { 143 .probe = imx_ocotp_probe, 144 .remove = imx_ocotp_remove, 145 .driver = { 146 .name = "imx_ocotp", 147 .of_match_table = imx_ocotp_dt_ids, 148 }, 149}; 150module_platform_driver(imx_ocotp_driver); 151 152MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); 153MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); 154MODULE_LICENSE("GPL v2"); 155