Commit | Line | Data |
---|---|---|
faa4fd2a BC |
1 | /* |
2 | * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com | |
3 | * Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | ||
22 | #include "solo6010.h" | |
23 | ||
24 | // #define SOLO_TEST_P2M | |
25 | ||
26 | int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, | |
27 | void *sys_addr, u32 ext_addr, u32 size) | |
28 | { | |
29 | dma_addr_t dma_addr; | |
30 | int ret; | |
31 | ||
32 | WARN_ON(!size); | |
33 | WARN_ON(id >= SOLO_NR_P2M); | |
34 | if (!size || id >= SOLO_NR_P2M) | |
35 | return -EINVAL; | |
36 | ||
37 | dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, | |
38 | wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); | |
39 | ||
40 | ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); | |
41 | ||
42 | pci_unmap_single(solo_dev->pdev, dma_addr, size, | |
43 | wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); | |
44 | ||
45 | return ret; | |
46 | } | |
47 | ||
48 | int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, | |
49 | dma_addr_t dma_addr, u32 ext_addr, u32 size) | |
50 | { | |
51 | struct solo_p2m_dev *p2m_dev; | |
52 | unsigned int timeout = 0; | |
53 | ||
54 | WARN_ON(!size); | |
55 | WARN_ON(id >= SOLO_NR_P2M); | |
56 | if (!size || id >= SOLO_NR_P2M) | |
57 | return -EINVAL; | |
58 | ||
59 | p2m_dev = &solo_dev->p2m_dev[id]; | |
60 | ||
61 | down(&p2m_dev->sem); | |
62 | ||
63 | start_dma: | |
64 | INIT_COMPLETION(p2m_dev->completion); | |
65 | p2m_dev->error = 0; | |
66 | solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), dma_addr); | |
67 | solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), ext_addr); | |
68 | solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id), | |
69 | SOLO_P2M_COPY_SIZE(size >> 2)); | |
70 | solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), | |
71 | SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | | |
72 | (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); | |
73 | ||
74 | timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); | |
75 | ||
76 | solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); | |
77 | ||
78 | /* XXX Really looks to me like we will get stuck here if a | |
79 | * real PCI P2M error occurs */ | |
80 | if (p2m_dev->error) | |
81 | goto start_dma; | |
82 | ||
83 | up(&p2m_dev->sem); | |
84 | ||
85 | return (timeout == 0) ? -EAGAIN : 0; | |
86 | } | |
87 | ||
88 | #ifdef SOLO_TEST_P2M | |
89 | ||
90 | #define P2M_TEST_CHAR 0xbe | |
91 | ||
92 | static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id, | |
93 | u32 base, int size) | |
94 | { | |
95 | u8 *wr_buf; | |
96 | u8 *rd_buf; | |
97 | int i; | |
98 | unsigned long long err_cnt = 0; | |
99 | ||
100 | wr_buf = kmalloc(size, GFP_KERNEL); | |
101 | if (!wr_buf) { | |
102 | printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); | |
103 | return size; | |
104 | } | |
105 | ||
106 | rd_buf = kmalloc(size, GFP_KERNEL); | |
107 | if (!rd_buf) { | |
108 | printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); | |
109 | kfree(wr_buf); | |
110 | return size; | |
111 | } | |
112 | ||
113 | memset(wr_buf, P2M_TEST_CHAR, size); | |
114 | memset(rd_buf, P2M_TEST_CHAR + 1, size); | |
115 | ||
116 | solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); | |
117 | solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); | |
118 | ||
119 | for (i = 0; i < size; i++) | |
120 | if (wr_buf[i] != rd_buf[i]) | |
121 | err_cnt++; | |
122 | ||
123 | kfree(wr_buf); | |
124 | kfree(rd_buf); | |
125 | ||
126 | return err_cnt; | |
127 | } | |
128 | ||
129 | #define TEST_CHUNK_SIZE (8 * 1024) | |
130 | ||
131 | static void run_p2m_test(struct solo6010_dev *solo_dev) | |
132 | { | |
133 | unsigned long long errs = 0; | |
134 | u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); | |
135 | int i, d; | |
136 | ||
137 | printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", | |
138 | SOLO6010_NAME, size); | |
139 | ||
140 | for (i = 0; i < size; i += TEST_CHUNK_SIZE) | |
141 | for (d = 0; d < 4; d++) | |
142 | errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); | |
143 | ||
144 | printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", | |
145 | SOLO6010_NAME, errs); | |
146 | ||
147 | return; | |
148 | } | |
149 | #else | |
150 | #define run_p2m_test(__solo) do{}while(0) | |
151 | #endif | |
152 | ||
153 | void solo_p2m_isr(struct solo6010_dev *solo_dev, int id) | |
154 | { | |
155 | solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); | |
156 | complete(&solo_dev->p2m_dev[id].completion); | |
157 | } | |
158 | ||
159 | void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status) | |
160 | { | |
161 | struct solo_p2m_dev *p2m_dev; | |
162 | int i; | |
163 | ||
164 | if (!(status & SOLO_PCI_ERR_P2M)) | |
165 | return; | |
166 | ||
167 | for (i = 0; i < SOLO_NR_P2M; i++) { | |
168 | p2m_dev = &solo_dev->p2m_dev[i]; | |
169 | p2m_dev->error = 1; | |
170 | solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); | |
171 | complete(&p2m_dev->completion); | |
172 | } | |
173 | } | |
174 | ||
175 | void solo_p2m_exit(struct solo6010_dev *solo_dev) | |
176 | { | |
177 | int i; | |
178 | ||
179 | for (i = 0; i < SOLO_NR_P2M; i++) | |
180 | solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i)); | |
181 | } | |
182 | ||
183 | int solo_p2m_init(struct solo6010_dev *solo_dev) | |
184 | { | |
185 | struct solo_p2m_dev *p2m_dev; | |
186 | int i; | |
187 | ||
188 | for (i = 0; i < SOLO_NR_P2M; i++) { | |
189 | p2m_dev = &solo_dev->p2m_dev[i]; | |
190 | ||
0f0800f1 | 191 | sema_init(&p2m_dev->sem, 1); |
faa4fd2a BC |
192 | init_completion(&p2m_dev->completion); |
193 | ||
194 | solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(i), | |
195 | __pa(p2m_dev->desc)); | |
196 | ||
197 | solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); | |
198 | solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), | |
199 | SOLO_P2M_CSC_16BIT_565 | | |
200 | SOLO_P2M_DMA_INTERVAL(0) | | |
201 | SOLO_P2M_PCI_MASTER_MODE); | |
202 | solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i)); | |
203 | } | |
204 | ||
205 | run_p2m_test(solo_dev); | |
206 | ||
207 | return 0; | |
208 | } |