15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2ad855eaeSCarlo Caione /* 39593ad32SMartin Blumenstingl * Amlogic Meson GX eFuse Driver 4ad855eaeSCarlo Caione * 5ad855eaeSCarlo Caione * Copyright (c) 2016 Endless Computers, Inc. 6ad855eaeSCarlo Caione * Author: Carlo Caione <carlo@endlessm.com> 7ad855eaeSCarlo Caione */ 8ad855eaeSCarlo Caione 9611fbca1SJerome Brunet #include <linux/clk.h> 10ad855eaeSCarlo Caione #include <linux/module.h> 11ad855eaeSCarlo Caione #include <linux/nvmem-provider.h> 12ad855eaeSCarlo Caione #include <linux/of.h> 13ad855eaeSCarlo Caione #include <linux/platform_device.h> 14ad855eaeSCarlo Caione 15ad855eaeSCarlo Caione #include <linux/firmware/meson/meson_sm.h> 16ad855eaeSCarlo Caione 17ad855eaeSCarlo Caione static int meson_efuse_read(void *context, unsigned int offset, 18ad855eaeSCarlo Caione void *val, size_t bytes) 19ad855eaeSCarlo Caione { 20*8cde3c21SCarlo Caione struct meson_sm_firmware *fw = context; 21*8cde3c21SCarlo Caione 22*8cde3c21SCarlo Caione return meson_sm_call_read(fw, (u8 *)val, bytes, SM_EFUSE_READ, offset, 23ad855eaeSCarlo Caione bytes, 0, 0, 0); 24ad855eaeSCarlo Caione } 25ad855eaeSCarlo Caione 26e5507730SJerome Brunet static int meson_efuse_write(void *context, unsigned int offset, 27e5507730SJerome Brunet void *val, size_t bytes) 28e5507730SJerome Brunet { 29*8cde3c21SCarlo Caione struct meson_sm_firmware *fw = context; 30*8cde3c21SCarlo Caione 31*8cde3c21SCarlo Caione return meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset, 32e5507730SJerome Brunet bytes, 0, 0, 0); 33e5507730SJerome Brunet } 34e5507730SJerome Brunet 35ad855eaeSCarlo Caione static const struct of_device_id meson_efuse_match[] = { 36ad855eaeSCarlo Caione { .compatible = "amlogic,meson-gxbb-efuse", }, 37ad855eaeSCarlo Caione { /* sentinel */ }, 38ad855eaeSCarlo Caione }; 39ad855eaeSCarlo Caione MODULE_DEVICE_TABLE(of, meson_efuse_match); 40ad855eaeSCarlo Caione 41ad855eaeSCarlo Caione static int meson_efuse_probe(struct platform_device *pdev) 42ad855eaeSCarlo Caione { 43401488d1SJerome Brunet struct device *dev = &pdev->dev; 44*8cde3c21SCarlo Caione struct meson_sm_firmware *fw; 45*8cde3c21SCarlo Caione struct device_node *sm_np; 46ad855eaeSCarlo Caione struct nvmem_device *nvmem; 47401488d1SJerome Brunet struct nvmem_config *econfig; 48611fbca1SJerome Brunet struct clk *clk; 49ad855eaeSCarlo Caione unsigned int size; 50611fbca1SJerome Brunet int ret; 51611fbca1SJerome Brunet 52*8cde3c21SCarlo Caione sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0); 53*8cde3c21SCarlo Caione if (!sm_np) { 54*8cde3c21SCarlo Caione dev_err(&pdev->dev, "no secure-monitor node\n"); 55*8cde3c21SCarlo Caione return -ENODEV; 56*8cde3c21SCarlo Caione } 57*8cde3c21SCarlo Caione 58*8cde3c21SCarlo Caione fw = meson_sm_get(sm_np); 59*8cde3c21SCarlo Caione of_node_put(sm_np); 60*8cde3c21SCarlo Caione if (!fw) 61*8cde3c21SCarlo Caione return -EPROBE_DEFER; 62*8cde3c21SCarlo Caione 63611fbca1SJerome Brunet clk = devm_clk_get(dev, NULL); 64611fbca1SJerome Brunet if (IS_ERR(clk)) { 65611fbca1SJerome Brunet ret = PTR_ERR(clk); 66611fbca1SJerome Brunet if (ret != -EPROBE_DEFER) 67611fbca1SJerome Brunet dev_err(dev, "failed to get efuse gate"); 68611fbca1SJerome Brunet return ret; 69611fbca1SJerome Brunet } 70611fbca1SJerome Brunet 71611fbca1SJerome Brunet ret = clk_prepare_enable(clk); 72611fbca1SJerome Brunet if (ret) { 73611fbca1SJerome Brunet dev_err(dev, "failed to enable gate"); 74611fbca1SJerome Brunet return ret; 75611fbca1SJerome Brunet } 76611fbca1SJerome Brunet 77611fbca1SJerome Brunet ret = devm_add_action_or_reset(dev, 78611fbca1SJerome Brunet (void(*)(void *))clk_disable_unprepare, 79611fbca1SJerome Brunet clk); 80611fbca1SJerome Brunet if (ret) { 81611fbca1SJerome Brunet dev_err(dev, "failed to add disable callback"); 82611fbca1SJerome Brunet return ret; 83611fbca1SJerome Brunet } 84ad855eaeSCarlo Caione 85*8cde3c21SCarlo Caione if (meson_sm_call(fw, SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) { 868649dbe5SJerome Brunet dev_err(dev, "failed to get max user"); 87ad855eaeSCarlo Caione return -EINVAL; 888649dbe5SJerome Brunet } 89ad855eaeSCarlo Caione 90401488d1SJerome Brunet econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); 91401488d1SJerome Brunet if (!econfig) 92401488d1SJerome Brunet return -ENOMEM; 93ad855eaeSCarlo Caione 94401488d1SJerome Brunet econfig->dev = dev; 95401488d1SJerome Brunet econfig->name = dev_name(dev); 96401488d1SJerome Brunet econfig->stride = 1; 97401488d1SJerome Brunet econfig->word_size = 1; 98401488d1SJerome Brunet econfig->reg_read = meson_efuse_read; 99e5507730SJerome Brunet econfig->reg_write = meson_efuse_write; 100401488d1SJerome Brunet econfig->size = size; 101*8cde3c21SCarlo Caione econfig->priv = fw; 102401488d1SJerome Brunet 103401488d1SJerome Brunet nvmem = devm_nvmem_register(&pdev->dev, econfig); 104ad855eaeSCarlo Caione 10590696a40SAndrey Smirnov return PTR_ERR_OR_ZERO(nvmem); 106ad855eaeSCarlo Caione } 107ad855eaeSCarlo Caione 108ad855eaeSCarlo Caione static struct platform_driver meson_efuse_driver = { 109ad855eaeSCarlo Caione .probe = meson_efuse_probe, 110ad855eaeSCarlo Caione .driver = { 111ad855eaeSCarlo Caione .name = "meson-efuse", 112ad855eaeSCarlo Caione .of_match_table = meson_efuse_match, 113ad855eaeSCarlo Caione }, 114ad855eaeSCarlo Caione }; 115ad855eaeSCarlo Caione 116ad855eaeSCarlo Caione module_platform_driver(meson_efuse_driver); 117ad855eaeSCarlo Caione 118ad855eaeSCarlo Caione MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); 1199593ad32SMartin Blumenstingl MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver"); 120ad855eaeSCarlo Caione MODULE_LICENSE("GPL v2"); 121