diff options
Diffstat (limited to 'drivers/staging/solo6x10/solo6010-p2m.c')
-rw-r--r-- | drivers/staging/solo6x10/solo6010-p2m.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/staging/solo6x10/solo6010-p2m.c b/drivers/staging/solo6x10/solo6010-p2m.c new file mode 100644 index 00000000000..1b81f069c7f --- /dev/null +++ b/drivers/staging/solo6x10/solo6010-p2m.c | |||
@@ -0,0 +1,208 @@ | |||
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 | |||
191 | init_MUTEX(&p2m_dev->sem); | ||
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 | } | ||