18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on drivers/misc/eeprom/sunxi_sid.c 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/kobject.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/random.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "fuse.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define FUSE_BEGIN 0x100 268c2ecf20Sopenharmony_ci#define FUSE_UID_LOW 0x08 278c2ecf20Sopenharmony_ci#define FUSE_UID_HIGH 0x0c 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic u32 tegra20_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return readl_relaxed(fuse->base + FUSE_BEGIN + offset); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void apb_dma_complete(void *args) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct tegra_fuse *fuse = args; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci complete(&fuse->apbdma.wait); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 448c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dma_desc; 458c2ecf20Sopenharmony_ci unsigned long time_left; 468c2ecf20Sopenharmony_ci u32 value = 0; 478c2ecf20Sopenharmony_ci int err; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci mutex_lock(&fuse->apbdma.lock); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci fuse->apbdma.config.src_addr = fuse->phys + FUSE_BEGIN + offset; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci err = dmaengine_slave_config(fuse->apbdma.chan, &fuse->apbdma.config); 548c2ecf20Sopenharmony_ci if (err) 558c2ecf20Sopenharmony_ci goto out; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dma_desc = dmaengine_prep_slave_single(fuse->apbdma.chan, 588c2ecf20Sopenharmony_ci fuse->apbdma.phys, 598c2ecf20Sopenharmony_ci sizeof(u32), DMA_DEV_TO_MEM, 608c2ecf20Sopenharmony_ci flags); 618c2ecf20Sopenharmony_ci if (!dma_desc) 628c2ecf20Sopenharmony_ci goto out; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dma_desc->callback = apb_dma_complete; 658c2ecf20Sopenharmony_ci dma_desc->callback_param = fuse; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci reinit_completion(&fuse->apbdma.wait); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci clk_prepare_enable(fuse->clk); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci dmaengine_submit(dma_desc); 728c2ecf20Sopenharmony_ci dma_async_issue_pending(fuse->apbdma.chan); 738c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&fuse->apbdma.wait, 748c2ecf20Sopenharmony_ci msecs_to_jiffies(50)); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (WARN(time_left == 0, "apb read dma timed out")) 778c2ecf20Sopenharmony_ci dmaengine_terminate_all(fuse->apbdma.chan); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci value = *fuse->apbdma.virt; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci clk_disable_unprepare(fuse->clk); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciout: 848c2ecf20Sopenharmony_ci mutex_unlock(&fuse->apbdma.lock); 858c2ecf20Sopenharmony_ci return value; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic bool dma_filter(struct dma_chan *chan, void *filter_param) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct device_node *np = chan->device->dev->of_node; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return of_device_is_compatible(np, "nvidia,tegra20-apbdma"); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int tegra20_fuse_probe(struct tegra_fuse *fuse) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci dma_cap_zero(mask); 1008c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci fuse->apbdma.chan = dma_request_channel(mask, dma_filter, NULL); 1038c2ecf20Sopenharmony_ci if (!fuse->apbdma.chan) 1048c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32), 1078c2ecf20Sopenharmony_ci &fuse->apbdma.phys, 1088c2ecf20Sopenharmony_ci GFP_KERNEL); 1098c2ecf20Sopenharmony_ci if (!fuse->apbdma.virt) { 1108c2ecf20Sopenharmony_ci dma_release_channel(fuse->apbdma.chan); 1118c2ecf20Sopenharmony_ci return -ENOMEM; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 1158c2ecf20Sopenharmony_ci fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 1168c2ecf20Sopenharmony_ci fuse->apbdma.config.src_maxburst = 1; 1178c2ecf20Sopenharmony_ci fuse->apbdma.config.dst_maxburst = 1; 1188c2ecf20Sopenharmony_ci fuse->apbdma.config.direction = DMA_DEV_TO_MEM; 1198c2ecf20Sopenharmony_ci fuse->apbdma.config.device_fc = false; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci init_completion(&fuse->apbdma.wait); 1228c2ecf20Sopenharmony_ci mutex_init(&fuse->apbdma.lock); 1238c2ecf20Sopenharmony_ci fuse->read = tegra20_fuse_read; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct tegra_fuse_info tegra20_fuse_info = { 1298c2ecf20Sopenharmony_ci .read = tegra20_fuse_read, 1308c2ecf20Sopenharmony_ci .size = 0x1f8, 1318c2ecf20Sopenharmony_ci .spare = 0x100, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* Early boot code. This code is called before the devices are created */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void __init tegra20_fuse_add_randomness(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u32 randomness[7]; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci randomness[0] = tegra_sku_info.sku_id; 1418c2ecf20Sopenharmony_ci randomness[1] = tegra_read_straps(); 1428c2ecf20Sopenharmony_ci randomness[2] = tegra_read_chipid(); 1438c2ecf20Sopenharmony_ci randomness[3] = tegra_sku_info.cpu_process_id << 16; 1448c2ecf20Sopenharmony_ci randomness[3] |= tegra_sku_info.soc_process_id; 1458c2ecf20Sopenharmony_ci randomness[4] = tegra_sku_info.cpu_speedo_id << 16; 1468c2ecf20Sopenharmony_ci randomness[4] |= tegra_sku_info.soc_speedo_id; 1478c2ecf20Sopenharmony_ci randomness[5] = tegra_fuse_read_early(FUSE_UID_LOW); 1488c2ecf20Sopenharmony_ci randomness[6] = tegra_fuse_read_early(FUSE_UID_HIGH); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci add_device_randomness(randomness, sizeof(randomness)); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void __init tegra20_fuse_init(struct tegra_fuse *fuse) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci fuse->read_early = tegra20_fuse_read_early; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci tegra_init_revision(); 1588c2ecf20Sopenharmony_ci fuse->soc->speedo_init(&tegra_sku_info); 1598c2ecf20Sopenharmony_ci tegra20_fuse_add_randomness(); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciconst struct tegra_fuse_soc tegra20_fuse_soc = { 1638c2ecf20Sopenharmony_ci .init = tegra20_fuse_init, 1648c2ecf20Sopenharmony_ci .speedo_init = tegra20_init_speedo_data, 1658c2ecf20Sopenharmony_ci .probe = tegra20_fuse_probe, 1668c2ecf20Sopenharmony_ci .info = &tegra20_fuse_info, 1678c2ecf20Sopenharmony_ci .soc_attr_group = &tegra_soc_attr_group, 1688c2ecf20Sopenharmony_ci}; 169