diff options
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/tegra/fuse/fuse-tegra20.c | 79 |
1 files changed, 76 insertions, 3 deletions
diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c index 7a9d0e045490..7cb63ab6aac2 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra20.c +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c | |||
@@ -18,6 +18,9 @@ | |||
18 | 18 | ||
19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
20 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
21 | #include <linux/completion.h> | ||
22 | #include <linux/dmaengine.h> | ||
23 | #include <linux/dma-mapping.h> | ||
21 | #include <linux/err.h> | 24 | #include <linux/err.h> |
22 | #include <linux/io.h> | 25 | #include <linux/io.h> |
23 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
@@ -39,18 +42,58 @@ static phys_addr_t fuse_phys; | |||
39 | static struct clk *fuse_clk; | 42 | static struct clk *fuse_clk; |
40 | static void __iomem __initdata *fuse_base; | 43 | static void __iomem __initdata *fuse_base; |
41 | 44 | ||
45 | static DEFINE_MUTEX(apb_dma_lock); | ||
46 | static DECLARE_COMPLETION(apb_dma_wait); | ||
47 | static struct dma_chan *apb_dma_chan; | ||
48 | static struct dma_slave_config dma_sconfig; | ||
49 | static u32 *apb_buffer; | ||
50 | static dma_addr_t apb_buffer_phys; | ||
51 | |||
52 | static void apb_dma_complete(void *args) | ||
53 | { | ||
54 | complete(&apb_dma_wait); | ||
55 | } | ||
56 | |||
42 | static u32 tegra20_fuse_readl(const unsigned int offset) | 57 | static u32 tegra20_fuse_readl(const unsigned int offset) |
43 | { | 58 | { |
44 | int ret; | 59 | int ret; |
45 | u32 val; | 60 | u32 val = 0; |
61 | struct dma_async_tx_descriptor *dma_desc; | ||
62 | |||
63 | mutex_lock(&apb_dma_lock); | ||
64 | |||
65 | dma_sconfig.src_addr = fuse_phys + FUSE_BEGIN + offset; | ||
66 | ret = dmaengine_slave_config(apb_dma_chan, &dma_sconfig); | ||
67 | if (ret) | ||
68 | goto out; | ||
69 | |||
70 | dma_desc = dmaengine_prep_slave_single(apb_dma_chan, apb_buffer_phys, | ||
71 | sizeof(u32), DMA_DEV_TO_MEM, | ||
72 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
73 | if (!dma_desc) | ||
74 | goto out; | ||
75 | |||
76 | dma_desc->callback = apb_dma_complete; | ||
77 | dma_desc->callback_param = NULL; | ||
78 | |||
79 | reinit_completion(&apb_dma_wait); | ||
46 | 80 | ||
47 | clk_prepare_enable(fuse_clk); | 81 | clk_prepare_enable(fuse_clk); |
48 | 82 | ||
49 | ret = tegra_apb_readl_using_dma(fuse_phys + FUSE_BEGIN + offset, &val); | 83 | dmaengine_submit(dma_desc); |
84 | dma_async_issue_pending(apb_dma_chan); | ||
85 | ret = wait_for_completion_timeout(&apb_dma_wait, msecs_to_jiffies(50)); | ||
86 | |||
87 | if (WARN(ret == 0, "apb read dma timed out")) | ||
88 | dmaengine_terminate_all(apb_dma_chan); | ||
89 | else | ||
90 | val = *apb_buffer; | ||
50 | 91 | ||
51 | clk_disable_unprepare(fuse_clk); | 92 | clk_disable_unprepare(fuse_clk); |
93 | out: | ||
94 | mutex_unlock(&apb_dma_lock); | ||
52 | 95 | ||
53 | return (ret < 0) ? 0 : val; | 96 | return val; |
54 | } | 97 | } |
55 | 98 | ||
56 | static const struct of_device_id tegra20_fuse_of_match[] = { | 99 | static const struct of_device_id tegra20_fuse_of_match[] = { |
@@ -58,9 +101,35 @@ static const struct of_device_id tegra20_fuse_of_match[] = { | |||
58 | {}, | 101 | {}, |
59 | }; | 102 | }; |
60 | 103 | ||
104 | static int apb_dma_init(void) | ||
105 | { | ||
106 | dma_cap_mask_t mask; | ||
107 | |||
108 | dma_cap_zero(mask); | ||
109 | dma_cap_set(DMA_SLAVE, mask); | ||
110 | apb_dma_chan = dma_request_channel(mask, NULL, NULL); | ||
111 | if (!apb_dma_chan) | ||
112 | return -EPROBE_DEFER; | ||
113 | |||
114 | apb_buffer = dma_alloc_coherent(NULL, sizeof(u32), &apb_buffer_phys, | ||
115 | GFP_KERNEL); | ||
116 | if (!apb_buffer) { | ||
117 | dma_release_channel(apb_dma_chan); | ||
118 | return -ENOMEM; | ||
119 | } | ||
120 | |||
121 | dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
122 | dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
123 | dma_sconfig.src_maxburst = 1; | ||
124 | dma_sconfig.dst_maxburst = 1; | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
61 | static int tegra20_fuse_probe(struct platform_device *pdev) | 129 | static int tegra20_fuse_probe(struct platform_device *pdev) |
62 | { | 130 | { |
63 | struct resource *res; | 131 | struct resource *res; |
132 | int err; | ||
64 | 133 | ||
65 | fuse_clk = devm_clk_get(&pdev->dev, NULL); | 134 | fuse_clk = devm_clk_get(&pdev->dev, NULL); |
66 | if (IS_ERR(fuse_clk)) { | 135 | if (IS_ERR(fuse_clk)) { |
@@ -73,6 +142,10 @@ static int tegra20_fuse_probe(struct platform_device *pdev) | |||
73 | return -EINVAL; | 142 | return -EINVAL; |
74 | fuse_phys = res->start; | 143 | fuse_phys = res->start; |
75 | 144 | ||
145 | err = apb_dma_init(); | ||
146 | if (err) | ||
147 | return err; | ||
148 | |||
76 | if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl)) | 149 | if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl)) |
77 | return -ENODEV; | 150 | return -ENODEV; |
78 | 151 | ||