root/drivers/soc/tegra/fuse/fuse-tegra20.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tegra20_fuse_read_early
  2. apb_dma_complete
  3. tegra20_fuse_read
  4. dma_filter
  5. tegra20_fuse_probe
  6. tegra20_fuse_add_randomness
  7. tegra20_fuse_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
   4  *
   5  * Based on drivers/misc/eeprom/sunxi_sid.c
   6  */
   7 
   8 #include <linux/device.h>
   9 #include <linux/clk.h>
  10 #include <linux/completion.h>
  11 #include <linux/dmaengine.h>
  12 #include <linux/dma-mapping.h>
  13 #include <linux/err.h>
  14 #include <linux/io.h>
  15 #include <linux/kernel.h>
  16 #include <linux/kobject.h>
  17 #include <linux/of_device.h>
  18 #include <linux/platform_device.h>
  19 #include <linux/random.h>
  20 
  21 #include <soc/tegra/fuse.h>
  22 
  23 #include "fuse.h"
  24 
  25 #define FUSE_BEGIN      0x100
  26 #define FUSE_UID_LOW    0x08
  27 #define FUSE_UID_HIGH   0x0c
  28 
  29 static u32 tegra20_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset)
  30 {
  31         return readl_relaxed(fuse->base + FUSE_BEGIN + offset);
  32 }
  33 
  34 static void apb_dma_complete(void *args)
  35 {
  36         struct tegra_fuse *fuse = args;
  37 
  38         complete(&fuse->apbdma.wait);
  39 }
  40 
  41 static u32 tegra20_fuse_read(struct tegra_fuse *fuse, unsigned int offset)
  42 {
  43         unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
  44         struct dma_async_tx_descriptor *dma_desc;
  45         unsigned long time_left;
  46         u32 value = 0;
  47         int err;
  48 
  49         mutex_lock(&fuse->apbdma.lock);
  50 
  51         fuse->apbdma.config.src_addr = fuse->phys + FUSE_BEGIN + offset;
  52 
  53         err = dmaengine_slave_config(fuse->apbdma.chan, &fuse->apbdma.config);
  54         if (err)
  55                 goto out;
  56 
  57         dma_desc = dmaengine_prep_slave_single(fuse->apbdma.chan,
  58                                                fuse->apbdma.phys,
  59                                                sizeof(u32), DMA_DEV_TO_MEM,
  60                                                flags);
  61         if (!dma_desc)
  62                 goto out;
  63 
  64         dma_desc->callback = apb_dma_complete;
  65         dma_desc->callback_param = fuse;
  66 
  67         reinit_completion(&fuse->apbdma.wait);
  68 
  69         clk_prepare_enable(fuse->clk);
  70 
  71         dmaengine_submit(dma_desc);
  72         dma_async_issue_pending(fuse->apbdma.chan);
  73         time_left = wait_for_completion_timeout(&fuse->apbdma.wait,
  74                                                 msecs_to_jiffies(50));
  75 
  76         if (WARN(time_left == 0, "apb read dma timed out"))
  77                 dmaengine_terminate_all(fuse->apbdma.chan);
  78         else
  79                 value = *fuse->apbdma.virt;
  80 
  81         clk_disable_unprepare(fuse->clk);
  82 
  83 out:
  84         mutex_unlock(&fuse->apbdma.lock);
  85         return value;
  86 }
  87 
  88 static bool dma_filter(struct dma_chan *chan, void *filter_param)
  89 {
  90         struct device_node *np = chan->device->dev->of_node;
  91 
  92         return of_device_is_compatible(np, "nvidia,tegra20-apbdma");
  93 }
  94 
  95 static int tegra20_fuse_probe(struct tegra_fuse *fuse)
  96 {
  97         dma_cap_mask_t mask;
  98 
  99         dma_cap_zero(mask);
 100         dma_cap_set(DMA_SLAVE, mask);
 101 
 102         fuse->apbdma.chan = dma_request_channel(mask, dma_filter, NULL);
 103         if (!fuse->apbdma.chan)
 104                 return -EPROBE_DEFER;
 105 
 106         fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32),
 107                                                &fuse->apbdma.phys,
 108                                                GFP_KERNEL);
 109         if (!fuse->apbdma.virt) {
 110                 dma_release_channel(fuse->apbdma.chan);
 111                 return -ENOMEM;
 112         }
 113 
 114         fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 115         fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 116         fuse->apbdma.config.src_maxburst = 1;
 117         fuse->apbdma.config.dst_maxburst = 1;
 118         fuse->apbdma.config.direction = DMA_DEV_TO_MEM;
 119         fuse->apbdma.config.device_fc = false;
 120 
 121         init_completion(&fuse->apbdma.wait);
 122         mutex_init(&fuse->apbdma.lock);
 123         fuse->read = tegra20_fuse_read;
 124 
 125         return 0;
 126 }
 127 
 128 static const struct tegra_fuse_info tegra20_fuse_info = {
 129         .read = tegra20_fuse_read,
 130         .size = 0x1f8,
 131         .spare = 0x100,
 132 };
 133 
 134 /* Early boot code. This code is called before the devices are created */
 135 
 136 static void __init tegra20_fuse_add_randomness(void)
 137 {
 138         u32 randomness[7];
 139 
 140         randomness[0] = tegra_sku_info.sku_id;
 141         randomness[1] = tegra_read_straps();
 142         randomness[2] = tegra_read_chipid();
 143         randomness[3] = tegra_sku_info.cpu_process_id << 16;
 144         randomness[3] |= tegra_sku_info.soc_process_id;
 145         randomness[4] = tegra_sku_info.cpu_speedo_id << 16;
 146         randomness[4] |= tegra_sku_info.soc_speedo_id;
 147         randomness[5] = tegra_fuse_read_early(FUSE_UID_LOW);
 148         randomness[6] = tegra_fuse_read_early(FUSE_UID_HIGH);
 149 
 150         add_device_randomness(randomness, sizeof(randomness));
 151 }
 152 
 153 static void __init tegra20_fuse_init(struct tegra_fuse *fuse)
 154 {
 155         fuse->read_early = tegra20_fuse_read_early;
 156 
 157         tegra_init_revision();
 158         fuse->soc->speedo_init(&tegra_sku_info);
 159         tegra20_fuse_add_randomness();
 160 }
 161 
 162 const struct tegra_fuse_soc tegra20_fuse_soc = {
 163         .init = tegra20_fuse_init,
 164         .speedo_init = tegra20_init_speedo_data,
 165         .probe = tegra20_fuse_probe,
 166         .info = &tegra20_fuse_info,
 167 };

/* [<][>][^][v][top][bottom][index][help] */