1*4a2addc2SPrasannaKumar Muralidharan // SPDX-License-Identifier: GPL-2.0-or-later 2*4a2addc2SPrasannaKumar Muralidharan /* 3*4a2addc2SPrasannaKumar Muralidharan * JZ4780 EFUSE Memory Support driver 4*4a2addc2SPrasannaKumar Muralidharan * 5*4a2addc2SPrasannaKumar Muralidharan * Copyright (c) 2017 PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com> 6*4a2addc2SPrasannaKumar Muralidharan * Copyright (c) 2020 H. Nikolaus Schaller <hns@goldelico.com> 7*4a2addc2SPrasannaKumar Muralidharan */ 8*4a2addc2SPrasannaKumar Muralidharan 9*4a2addc2SPrasannaKumar Muralidharan /* 10*4a2addc2SPrasannaKumar Muralidharan * Currently supports JZ4780 efuse which has 8K programmable bit. 11*4a2addc2SPrasannaKumar Muralidharan * Efuse is separated into seven segments as below: 12*4a2addc2SPrasannaKumar Muralidharan * 13*4a2addc2SPrasannaKumar Muralidharan * ----------------------------------------------------------------------- 14*4a2addc2SPrasannaKumar Muralidharan * | 64 bit | 128 bit | 128 bit | 3520 bit | 8 bit | 2296 bit | 2048 bit | 15*4a2addc2SPrasannaKumar Muralidharan * ----------------------------------------------------------------------- 16*4a2addc2SPrasannaKumar Muralidharan * 17*4a2addc2SPrasannaKumar Muralidharan * The rom itself is accessed using a 9 bit address line and an 8 word wide bus 18*4a2addc2SPrasannaKumar Muralidharan * which reads/writes based on strobes. The strobe is configured in the config 19*4a2addc2SPrasannaKumar Muralidharan * register and is based on number of cycles of the bus clock. 20*4a2addc2SPrasannaKumar Muralidharan * 21*4a2addc2SPrasannaKumar Muralidharan * Driver supports read only as the writes are done in the Factory. 22*4a2addc2SPrasannaKumar Muralidharan */ 23*4a2addc2SPrasannaKumar Muralidharan 24*4a2addc2SPrasannaKumar Muralidharan #include <linux/bitops.h> 25*4a2addc2SPrasannaKumar Muralidharan #include <linux/clk.h> 26*4a2addc2SPrasannaKumar Muralidharan #include <linux/module.h> 27*4a2addc2SPrasannaKumar Muralidharan #include <linux/nvmem-provider.h> 28*4a2addc2SPrasannaKumar Muralidharan #include <linux/of.h> 29*4a2addc2SPrasannaKumar Muralidharan #include <linux/platform_device.h> 30*4a2addc2SPrasannaKumar Muralidharan #include <linux/regmap.h> 31*4a2addc2SPrasannaKumar Muralidharan #include <linux/timer.h> 32*4a2addc2SPrasannaKumar Muralidharan 33*4a2addc2SPrasannaKumar Muralidharan #define JZ_EFUCTRL (0x0) /* Control Register */ 34*4a2addc2SPrasannaKumar Muralidharan #define JZ_EFUCFG (0x4) /* Configure Register*/ 35*4a2addc2SPrasannaKumar Muralidharan #define JZ_EFUSTATE (0x8) /* Status Register */ 36*4a2addc2SPrasannaKumar Muralidharan #define JZ_EFUDATA(n) (0xC + (n) * 4) 37*4a2addc2SPrasannaKumar Muralidharan 38*4a2addc2SPrasannaKumar Muralidharan /* We read 32 byte chunks to avoid complexity in the driver. */ 39*4a2addc2SPrasannaKumar Muralidharan #define JZ_EFU_READ_SIZE 32 40*4a2addc2SPrasannaKumar Muralidharan 41*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_ADDR_MASK 0x3FF 42*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_ADDR_SHIFT 21 43*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_LEN_MASK 0x1F 44*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_LEN_SHIFT 16 45*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_PG_EN BIT(15) 46*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_WR_EN BIT(1) 47*4a2addc2SPrasannaKumar Muralidharan #define EFUCTRL_RD_EN BIT(0) 48*4a2addc2SPrasannaKumar Muralidharan 49*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_INT_EN BIT(31) 50*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_RD_ADJ_MASK 0xF 51*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_RD_ADJ_SHIFT 20 52*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_RD_STR_MASK 0xF 53*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_RD_STR_SHIFT 16 54*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_WR_ADJ_MASK 0xF 55*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_WR_ADJ_SHIFT 12 56*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_WR_STR_MASK 0xFFF 57*4a2addc2SPrasannaKumar Muralidharan #define EFUCFG_WR_STR_SHIFT 0 58*4a2addc2SPrasannaKumar Muralidharan 59*4a2addc2SPrasannaKumar Muralidharan #define EFUSTATE_WR_DONE BIT(1) 60*4a2addc2SPrasannaKumar Muralidharan #define EFUSTATE_RD_DONE BIT(0) 61*4a2addc2SPrasannaKumar Muralidharan 62*4a2addc2SPrasannaKumar Muralidharan struct jz4780_efuse { 63*4a2addc2SPrasannaKumar Muralidharan struct device *dev; 64*4a2addc2SPrasannaKumar Muralidharan struct regmap *map; 65*4a2addc2SPrasannaKumar Muralidharan struct clk *clk; 66*4a2addc2SPrasannaKumar Muralidharan }; 67*4a2addc2SPrasannaKumar Muralidharan 68*4a2addc2SPrasannaKumar Muralidharan /* main entry point */ 69*4a2addc2SPrasannaKumar Muralidharan static int jz4780_efuse_read(void *context, unsigned int offset, 70*4a2addc2SPrasannaKumar Muralidharan void *val, size_t bytes) 71*4a2addc2SPrasannaKumar Muralidharan { 72*4a2addc2SPrasannaKumar Muralidharan struct jz4780_efuse *efuse = context; 73*4a2addc2SPrasannaKumar Muralidharan 74*4a2addc2SPrasannaKumar Muralidharan while (bytes > 0) { 75*4a2addc2SPrasannaKumar Muralidharan unsigned int start = offset & ~(JZ_EFU_READ_SIZE - 1); 76*4a2addc2SPrasannaKumar Muralidharan unsigned int chunk = min(bytes, (start + JZ_EFU_READ_SIZE) 77*4a2addc2SPrasannaKumar Muralidharan - offset); 78*4a2addc2SPrasannaKumar Muralidharan char buf[JZ_EFU_READ_SIZE]; 79*4a2addc2SPrasannaKumar Muralidharan unsigned int tmp; 80*4a2addc2SPrasannaKumar Muralidharan u32 ctrl; 81*4a2addc2SPrasannaKumar Muralidharan int ret; 82*4a2addc2SPrasannaKumar Muralidharan 83*4a2addc2SPrasannaKumar Muralidharan ctrl = (start << EFUCTRL_ADDR_SHIFT) 84*4a2addc2SPrasannaKumar Muralidharan | ((JZ_EFU_READ_SIZE - 1) << EFUCTRL_LEN_SHIFT) 85*4a2addc2SPrasannaKumar Muralidharan | EFUCTRL_RD_EN; 86*4a2addc2SPrasannaKumar Muralidharan 87*4a2addc2SPrasannaKumar Muralidharan regmap_update_bits(efuse->map, JZ_EFUCTRL, 88*4a2addc2SPrasannaKumar Muralidharan (EFUCTRL_ADDR_MASK << EFUCTRL_ADDR_SHIFT) | 89*4a2addc2SPrasannaKumar Muralidharan (EFUCTRL_LEN_MASK << EFUCTRL_LEN_SHIFT) | 90*4a2addc2SPrasannaKumar Muralidharan EFUCTRL_PG_EN | EFUCTRL_WR_EN | 91*4a2addc2SPrasannaKumar Muralidharan EFUCTRL_RD_EN, 92*4a2addc2SPrasannaKumar Muralidharan ctrl); 93*4a2addc2SPrasannaKumar Muralidharan 94*4a2addc2SPrasannaKumar Muralidharan ret = regmap_read_poll_timeout(efuse->map, JZ_EFUSTATE, 95*4a2addc2SPrasannaKumar Muralidharan tmp, tmp & EFUSTATE_RD_DONE, 96*4a2addc2SPrasannaKumar Muralidharan 1 * MSEC_PER_SEC, 97*4a2addc2SPrasannaKumar Muralidharan 50 * MSEC_PER_SEC); 98*4a2addc2SPrasannaKumar Muralidharan if (ret < 0) { 99*4a2addc2SPrasannaKumar Muralidharan dev_err(efuse->dev, "Time out while reading efuse data"); 100*4a2addc2SPrasannaKumar Muralidharan return ret; 101*4a2addc2SPrasannaKumar Muralidharan } 102*4a2addc2SPrasannaKumar Muralidharan 103*4a2addc2SPrasannaKumar Muralidharan ret = regmap_bulk_read(efuse->map, JZ_EFUDATA(0), 104*4a2addc2SPrasannaKumar Muralidharan buf, JZ_EFU_READ_SIZE / sizeof(u32)); 105*4a2addc2SPrasannaKumar Muralidharan if (ret < 0) 106*4a2addc2SPrasannaKumar Muralidharan return ret; 107*4a2addc2SPrasannaKumar Muralidharan 108*4a2addc2SPrasannaKumar Muralidharan memcpy(val, &buf[offset - start], chunk); 109*4a2addc2SPrasannaKumar Muralidharan 110*4a2addc2SPrasannaKumar Muralidharan val += chunk; 111*4a2addc2SPrasannaKumar Muralidharan offset += chunk; 112*4a2addc2SPrasannaKumar Muralidharan bytes -= chunk; 113*4a2addc2SPrasannaKumar Muralidharan } 114*4a2addc2SPrasannaKumar Muralidharan 115*4a2addc2SPrasannaKumar Muralidharan return 0; 116*4a2addc2SPrasannaKumar Muralidharan } 117*4a2addc2SPrasannaKumar Muralidharan 118*4a2addc2SPrasannaKumar Muralidharan static struct nvmem_config jz4780_efuse_nvmem_config = { 119*4a2addc2SPrasannaKumar Muralidharan .name = "jz4780-efuse", 120*4a2addc2SPrasannaKumar Muralidharan .size = 1024, 121*4a2addc2SPrasannaKumar Muralidharan .word_size = 1, 122*4a2addc2SPrasannaKumar Muralidharan .stride = 1, 123*4a2addc2SPrasannaKumar Muralidharan .owner = THIS_MODULE, 124*4a2addc2SPrasannaKumar Muralidharan .reg_read = jz4780_efuse_read, 125*4a2addc2SPrasannaKumar Muralidharan }; 126*4a2addc2SPrasannaKumar Muralidharan 127*4a2addc2SPrasannaKumar Muralidharan static const struct regmap_config jz4780_efuse_regmap_config = { 128*4a2addc2SPrasannaKumar Muralidharan .reg_bits = 32, 129*4a2addc2SPrasannaKumar Muralidharan .val_bits = 32, 130*4a2addc2SPrasannaKumar Muralidharan .reg_stride = 4, 131*4a2addc2SPrasannaKumar Muralidharan .max_register = JZ_EFUDATA(7), 132*4a2addc2SPrasannaKumar Muralidharan }; 133*4a2addc2SPrasannaKumar Muralidharan 134*4a2addc2SPrasannaKumar Muralidharan static void clk_disable_unprepare_helper(void *clock) 135*4a2addc2SPrasannaKumar Muralidharan { 136*4a2addc2SPrasannaKumar Muralidharan clk_disable_unprepare(clock); 137*4a2addc2SPrasannaKumar Muralidharan } 138*4a2addc2SPrasannaKumar Muralidharan 139*4a2addc2SPrasannaKumar Muralidharan static int jz4780_efuse_probe(struct platform_device *pdev) 140*4a2addc2SPrasannaKumar Muralidharan { 141*4a2addc2SPrasannaKumar Muralidharan struct nvmem_device *nvmem; 142*4a2addc2SPrasannaKumar Muralidharan struct jz4780_efuse *efuse; 143*4a2addc2SPrasannaKumar Muralidharan struct nvmem_config cfg; 144*4a2addc2SPrasannaKumar Muralidharan unsigned long clk_rate; 145*4a2addc2SPrasannaKumar Muralidharan unsigned long rd_adj; 146*4a2addc2SPrasannaKumar Muralidharan unsigned long rd_strobe; 147*4a2addc2SPrasannaKumar Muralidharan struct device *dev = &pdev->dev; 148*4a2addc2SPrasannaKumar Muralidharan void __iomem *regs; 149*4a2addc2SPrasannaKumar Muralidharan int ret; 150*4a2addc2SPrasannaKumar Muralidharan 151*4a2addc2SPrasannaKumar Muralidharan efuse = devm_kzalloc(dev, sizeof(*efuse), GFP_KERNEL); 152*4a2addc2SPrasannaKumar Muralidharan if (!efuse) 153*4a2addc2SPrasannaKumar Muralidharan return -ENOMEM; 154*4a2addc2SPrasannaKumar Muralidharan 155*4a2addc2SPrasannaKumar Muralidharan regs = devm_platform_ioremap_resource(pdev, 0); 156*4a2addc2SPrasannaKumar Muralidharan if (IS_ERR(regs)) 157*4a2addc2SPrasannaKumar Muralidharan return PTR_ERR(regs); 158*4a2addc2SPrasannaKumar Muralidharan 159*4a2addc2SPrasannaKumar Muralidharan efuse->map = devm_regmap_init_mmio(dev, regs, 160*4a2addc2SPrasannaKumar Muralidharan &jz4780_efuse_regmap_config); 161*4a2addc2SPrasannaKumar Muralidharan if (IS_ERR(efuse->map)) 162*4a2addc2SPrasannaKumar Muralidharan return PTR_ERR(efuse->map); 163*4a2addc2SPrasannaKumar Muralidharan 164*4a2addc2SPrasannaKumar Muralidharan efuse->clk = devm_clk_get(&pdev->dev, NULL); 165*4a2addc2SPrasannaKumar Muralidharan if (IS_ERR(efuse->clk)) 166*4a2addc2SPrasannaKumar Muralidharan return PTR_ERR(efuse->clk); 167*4a2addc2SPrasannaKumar Muralidharan 168*4a2addc2SPrasannaKumar Muralidharan ret = clk_prepare_enable(efuse->clk); 169*4a2addc2SPrasannaKumar Muralidharan if (ret < 0) 170*4a2addc2SPrasannaKumar Muralidharan return ret; 171*4a2addc2SPrasannaKumar Muralidharan 172*4a2addc2SPrasannaKumar Muralidharan ret = devm_add_action_or_reset(&pdev->dev, 173*4a2addc2SPrasannaKumar Muralidharan clk_disable_unprepare_helper, 174*4a2addc2SPrasannaKumar Muralidharan efuse->clk); 175*4a2addc2SPrasannaKumar Muralidharan if (ret < 0) 176*4a2addc2SPrasannaKumar Muralidharan return ret; 177*4a2addc2SPrasannaKumar Muralidharan 178*4a2addc2SPrasannaKumar Muralidharan clk_rate = clk_get_rate(efuse->clk); 179*4a2addc2SPrasannaKumar Muralidharan 180*4a2addc2SPrasannaKumar Muralidharan efuse->dev = dev; 181*4a2addc2SPrasannaKumar Muralidharan 182*4a2addc2SPrasannaKumar Muralidharan /* 183*4a2addc2SPrasannaKumar Muralidharan * rd_adj and rd_strobe are 4 bit values 184*4a2addc2SPrasannaKumar Muralidharan * conditions: 185*4a2addc2SPrasannaKumar Muralidharan * bus clk_period * (rd_adj + 1) > 6.5ns 186*4a2addc2SPrasannaKumar Muralidharan * bus clk_period * (rd_adj + 5 + rd_strobe) > 35ns 187*4a2addc2SPrasannaKumar Muralidharan * i.e. rd_adj >= 6.5ns / clk_period 188*4a2addc2SPrasannaKumar Muralidharan * i.e. rd_strobe >= 35 ns / clk_period - 5 - rd_adj + 1 189*4a2addc2SPrasannaKumar Muralidharan * constants: 190*4a2addc2SPrasannaKumar Muralidharan * 1 / 6.5ns == 153846154 Hz 191*4a2addc2SPrasannaKumar Muralidharan * 1 / 35ns == 28571429 Hz 192*4a2addc2SPrasannaKumar Muralidharan */ 193*4a2addc2SPrasannaKumar Muralidharan 194*4a2addc2SPrasannaKumar Muralidharan rd_adj = clk_rate / 153846154; 195*4a2addc2SPrasannaKumar Muralidharan rd_strobe = clk_rate / 28571429 - 5 - rd_adj + 1; 196*4a2addc2SPrasannaKumar Muralidharan 197*4a2addc2SPrasannaKumar Muralidharan if (rd_adj > EFUCFG_RD_ADJ_MASK || 198*4a2addc2SPrasannaKumar Muralidharan rd_strobe > EFUCFG_RD_STR_MASK) { 199*4a2addc2SPrasannaKumar Muralidharan dev_err(&pdev->dev, "Cannot set clock configuration\n"); 200*4a2addc2SPrasannaKumar Muralidharan return -EINVAL; 201*4a2addc2SPrasannaKumar Muralidharan } 202*4a2addc2SPrasannaKumar Muralidharan 203*4a2addc2SPrasannaKumar Muralidharan regmap_update_bits(efuse->map, JZ_EFUCFG, 204*4a2addc2SPrasannaKumar Muralidharan (EFUCFG_RD_ADJ_MASK << EFUCFG_RD_ADJ_SHIFT) | 205*4a2addc2SPrasannaKumar Muralidharan (EFUCFG_RD_STR_MASK << EFUCFG_RD_STR_SHIFT), 206*4a2addc2SPrasannaKumar Muralidharan (rd_adj << EFUCFG_RD_ADJ_SHIFT) | 207*4a2addc2SPrasannaKumar Muralidharan (rd_strobe << EFUCFG_RD_STR_SHIFT)); 208*4a2addc2SPrasannaKumar Muralidharan 209*4a2addc2SPrasannaKumar Muralidharan cfg = jz4780_efuse_nvmem_config; 210*4a2addc2SPrasannaKumar Muralidharan cfg.dev = &pdev->dev; 211*4a2addc2SPrasannaKumar Muralidharan cfg.priv = efuse; 212*4a2addc2SPrasannaKumar Muralidharan 213*4a2addc2SPrasannaKumar Muralidharan nvmem = devm_nvmem_register(dev, &cfg); 214*4a2addc2SPrasannaKumar Muralidharan if (IS_ERR(nvmem)) 215*4a2addc2SPrasannaKumar Muralidharan return PTR_ERR(nvmem); 216*4a2addc2SPrasannaKumar Muralidharan 217*4a2addc2SPrasannaKumar Muralidharan return 0; 218*4a2addc2SPrasannaKumar Muralidharan } 219*4a2addc2SPrasannaKumar Muralidharan 220*4a2addc2SPrasannaKumar Muralidharan static const struct of_device_id jz4780_efuse_match[] = { 221*4a2addc2SPrasannaKumar Muralidharan { .compatible = "ingenic,jz4780-efuse" }, 222*4a2addc2SPrasannaKumar Muralidharan { /* sentinel */ }, 223*4a2addc2SPrasannaKumar Muralidharan }; 224*4a2addc2SPrasannaKumar Muralidharan MODULE_DEVICE_TABLE(of, jz4780_efuse_match); 225*4a2addc2SPrasannaKumar Muralidharan 226*4a2addc2SPrasannaKumar Muralidharan static struct platform_driver jz4780_efuse_driver = { 227*4a2addc2SPrasannaKumar Muralidharan .probe = jz4780_efuse_probe, 228*4a2addc2SPrasannaKumar Muralidharan .driver = { 229*4a2addc2SPrasannaKumar Muralidharan .name = "jz4780-efuse", 230*4a2addc2SPrasannaKumar Muralidharan .of_match_table = jz4780_efuse_match, 231*4a2addc2SPrasannaKumar Muralidharan }, 232*4a2addc2SPrasannaKumar Muralidharan }; 233*4a2addc2SPrasannaKumar Muralidharan module_platform_driver(jz4780_efuse_driver); 234*4a2addc2SPrasannaKumar Muralidharan 235*4a2addc2SPrasannaKumar Muralidharan MODULE_AUTHOR("PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>"); 236*4a2addc2SPrasannaKumar Muralidharan MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); 237*4a2addc2SPrasannaKumar Muralidharan MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 238*4a2addc2SPrasannaKumar Muralidharan MODULE_DESCRIPTION("Ingenic JZ4780 efuse driver"); 239*4a2addc2SPrasannaKumar Muralidharan MODULE_LICENSE("GPL v2"); 240