1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved.
5 * Based on drivers/misc/eeprom/sunxi_sid.c
8 #include <linux/device.h>
10 #include <linux/completion.h>
11 #include <linux/dmaengine.h>
12 #include <linux/dma-mapping.h>
13 #include <linux/err.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>
21 #include <soc/tegra/fuse.h>
25 #define FUSE_BEGIN 0x100
26 #define FUSE_UID_LOW 0x08
27 #define FUSE_UID_HIGH 0x0c
29 static u32
tegra20_fuse_read_early(struct tegra_fuse
*fuse
, unsigned int offset
)
31 return readl_relaxed(fuse
->base
+ FUSE_BEGIN
+ offset
);
34 static void apb_dma_complete(void *args
)
36 struct tegra_fuse
*fuse
= args
;
38 complete(&fuse
->apbdma
.wait
);
41 static u32
tegra20_fuse_read(struct tegra_fuse
*fuse
, unsigned int offset
)
43 unsigned long flags
= DMA_PREP_INTERRUPT
| DMA_CTRL_ACK
;
44 struct dma_async_tx_descriptor
*dma_desc
;
45 unsigned long time_left
;
49 mutex_lock(&fuse
->apbdma
.lock
);
51 fuse
->apbdma
.config
.src_addr
= fuse
->phys
+ FUSE_BEGIN
+ offset
;
53 err
= dmaengine_slave_config(fuse
->apbdma
.chan
, &fuse
->apbdma
.config
);
57 dma_desc
= dmaengine_prep_slave_single(fuse
->apbdma
.chan
,
59 sizeof(u32
), DMA_DEV_TO_MEM
,
64 dma_desc
->callback
= apb_dma_complete
;
65 dma_desc
->callback_param
= fuse
;
67 reinit_completion(&fuse
->apbdma
.wait
);
69 clk_prepare_enable(fuse
->clk
);
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));
76 if (WARN(time_left
== 0, "apb read dma timed out"))
77 dmaengine_terminate_all(fuse
->apbdma
.chan
);
79 value
= *fuse
->apbdma
.virt
;
81 clk_disable_unprepare(fuse
->clk
);
84 mutex_unlock(&fuse
->apbdma
.lock
);
88 static bool dma_filter(struct dma_chan
*chan
, void *filter_param
)
90 struct device_node
*np
= chan
->device
->dev
->of_node
;
92 return of_device_is_compatible(np
, "nvidia,tegra20-apbdma");
95 static int tegra20_fuse_probe(struct tegra_fuse
*fuse
)
100 dma_cap_set(DMA_SLAVE
, mask
);
102 fuse
->apbdma
.chan
= dma_request_channel(mask
, dma_filter
, NULL
);
103 if (!fuse
->apbdma
.chan
)
104 return -EPROBE_DEFER
;
106 fuse
->apbdma
.virt
= dma_alloc_coherent(fuse
->dev
, sizeof(u32
),
109 if (!fuse
->apbdma
.virt
) {
110 dma_release_channel(fuse
->apbdma
.chan
);
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;
121 init_completion(&fuse
->apbdma
.wait
);
122 mutex_init(&fuse
->apbdma
.lock
);
123 fuse
->read
= tegra20_fuse_read
;
128 static const struct tegra_fuse_info tegra20_fuse_info
= {
129 .read
= tegra20_fuse_read
,
134 /* Early boot code. This code is called before the devices are created */
136 static void __init
tegra20_fuse_add_randomness(void)
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
);
150 add_device_randomness(randomness
, sizeof(randomness
));
153 static void __init
tegra20_fuse_init(struct tegra_fuse
*fuse
)
155 fuse
->read_early
= tegra20_fuse_read_early
;
157 tegra_init_revision();
158 fuse
->soc
->speedo_init(&tegra_sku_info
);
159 tegra20_fuse_add_randomness();
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 .soc_attr_group
= &tegra_soc_attr_group
,