blob: 20be9571990a30410816f8847e48ea59e273ae2e [file] [log] [blame]
Jarod Wilson7963eb42010-01-04 18:02:27 -05001/***************************************************************************
Ameya Palande21b08382010-02-24 20:18:28 +02002 BCM70010 Linux driver
3 Copyright (c) 2005-2009, Broadcom Corporation.
4
5 This driver 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, version 2 of the License.
8
9 This driver is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this driver. If not, see <http://www.gnu.org/licenses/>.
16***************************************************************************/
Jarod Wilson7963eb42010-01-04 18:02:27 -050017
Jorgyano Vieira01c32072012-02-25 21:58:21 -020018#include "crystalhd.h"
19
Arnd Bergmann8e2394a2010-07-11 23:18:52 +020020#include <linux/mutex.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Jarod Wilson7963eb42010-01-04 18:02:27 -050022
Jarod Wilson7963eb42010-01-04 18:02:27 -050023
Arnd Bergmann8e2394a2010-07-11 23:18:52 +020024static DEFINE_MUTEX(chd_dec_mutex);
Jarod Wilson7963eb42010-01-04 18:02:27 -050025static struct class *crystalhd_class;
26
27static struct crystalhd_adp *g_adp_info;
28
29static irqreturn_t chd_dec_isr(int irq, void *arg)
30{
31 struct crystalhd_adp *adp = (struct crystalhd_adp *) arg;
32 int rc = 0;
33 if (adp)
34 rc = crystalhd_cmd_interrupt(&adp->cmds);
35
36 return IRQ_RETVAL(rc);
37}
38
39static int chd_dec_enable_int(struct crystalhd_adp *adp)
40{
41 int rc = 0;
42
43 if (!adp || !adp->pdev) {
44 BCMLOG_ERR("Invalid arg!!\n");
45 return -EINVAL;
46 }
47
48 if (adp->pdev->msi_enabled)
49 adp->msi = 1;
50 else
51 adp->msi = pci_enable_msi(adp->pdev);
52
53 rc = request_irq(adp->pdev->irq, chd_dec_isr, IRQF_SHARED,
54 adp->name, (void *)adp);
55 if (rc) {
Lars Lindley641b63f2010-03-11 00:21:20 +010056 BCMLOG_ERR("Interrupt request failed..\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -050057 pci_disable_msi(adp->pdev);
58 }
59
60 return rc;
61}
62
63static int chd_dec_disable_int(struct crystalhd_adp *adp)
64{
65 if (!adp || !adp->pdev) {
66 BCMLOG_ERR("Invalid arg!!\n");
67 return -EINVAL;
68 }
69
70 free_irq(adp->pdev->irq, adp);
71
72 if (adp->msi)
73 pci_disable_msi(adp->pdev);
74
75 return 0;
76}
77
Valentina Manea038d3f82013-10-07 23:12:14 +030078static struct
79crystalhd_ioctl_data *chd_dec_alloc_iodata(struct crystalhd_adp *adp,
80 bool isr)
Jarod Wilson7963eb42010-01-04 18:02:27 -050081{
82 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +030083 struct crystalhd_ioctl_data *temp;
Jarod Wilson7963eb42010-01-04 18:02:27 -050084
85 if (!adp)
86 return NULL;
87
88 spin_lock_irqsave(&adp->lock, flags);
89
90 temp = adp->idata_free_head;
91 if (temp) {
92 adp->idata_free_head = adp->idata_free_head->next;
93 memset(temp, 0, sizeof(*temp));
94 }
95
96 spin_unlock_irqrestore(&adp->lock, flags);
97 return temp;
98}
99
Valentina Manea038d3f82013-10-07 23:12:14 +0300100static void chd_dec_free_iodata(struct crystalhd_adp *adp,
101 struct crystalhd_ioctl_data *iodata, bool isr)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500102{
103 unsigned long flags = 0;
104
105 if (!adp || !iodata)
106 return;
107
108 spin_lock_irqsave(&adp->lock, flags);
109 iodata->next = adp->idata_free_head;
110 adp->idata_free_head = iodata;
111 spin_unlock_irqrestore(&adp->lock, flags);
112}
113
Monam Agarwal9df2b0d2014-03-05 06:13:54 +0530114static inline int crystalhd_user_data(void __user *ud, void *dr,
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700115 int size, int set)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500116{
117 int rc;
118
119 if (!ud || !dr) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100120 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500121 return -EINVAL;
122 }
123
124 if (set)
Monam Agarwal9df2b0d2014-03-05 06:13:54 +0530125 rc = copy_to_user(ud, dr, size);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500126 else
Monam Agarwal9df2b0d2014-03-05 06:13:54 +0530127 rc = copy_from_user(dr, ud, size);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500128
129 if (rc) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100130 BCMLOG_ERR("Invalid args for command\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500131 rc = -EFAULT;
132 }
133
134 return rc;
135}
136
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700137static int chd_dec_fetch_cdata(struct crystalhd_adp *adp,
138 struct crystalhd_ioctl_data *io, uint32_t m_sz, unsigned long ua)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500139{
140 unsigned long ua_off;
141 int rc = 0;
142
143 if (!adp || !io || !ua || !m_sz) {
144 BCMLOG_ERR("Invalid Arg!!\n");
145 return -EINVAL;
146 }
147
148 io->add_cdata = vmalloc(m_sz);
149 if (!io->add_cdata) {
150 BCMLOG_ERR("kalloc fail for sz:%x\n", m_sz);
151 return -ENOMEM;
152 }
153
154 io->add_cdata_sz = m_sz;
155 ua_off = ua + sizeof(io->udata);
Aybuke Ozdemir80b23922014-03-14 23:44:54 +0200156 rc = crystalhd_user_data((void __user *)ua_off, io->add_cdata,
157 io->add_cdata_sz, 0);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500158 if (rc) {
159 BCMLOG_ERR("failed to pull add_cdata sz:%x ua_off:%x\n",
160 io->add_cdata_sz, (unsigned int)ua_off);
Wei Yongjuna7d7b012013-10-11 12:39:28 +0800161 vfree(io->add_cdata);
Scott Kidder61679442010-05-26 23:38:45 -0500162 io->add_cdata = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500163 return -ENODATA;
164 }
165
166 return rc;
167}
168
169static int chd_dec_release_cdata(struct crystalhd_adp *adp,
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700170 struct crystalhd_ioctl_data *io, unsigned long ua)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500171{
172 unsigned long ua_off;
173 int rc;
174
175 if (!adp || !io || !ua) {
176 BCMLOG_ERR("Invalid Arg!!\n");
177 return -EINVAL;
178 }
179
180 if (io->cmd != BCM_IOC_FW_DOWNLOAD) {
181 ua_off = ua + sizeof(io->udata);
Monam Agarwal9df2b0d2014-03-05 06:13:54 +0530182 rc = crystalhd_user_data((void __user *)ua_off, io->add_cdata,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500183 io->add_cdata_sz, 1);
184 if (rc) {
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700185 BCMLOG_ERR(
186 "failed to push add_cdata sz:%x ua_off:%x\n",
187 io->add_cdata_sz, (unsigned int)ua_off);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500188 return -ENODATA;
189 }
190 }
191
192 if (io->add_cdata) {
193 vfree(io->add_cdata);
194 io->add_cdata = NULL;
195 }
196
197 return 0;
198}
199
200static int chd_dec_proc_user_data(struct crystalhd_adp *adp,
Lior Dotanabfc7682010-05-18 12:46:42 +0300201 struct crystalhd_ioctl_data *io,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500202 unsigned long ua, int set)
203{
204 int rc;
205 uint32_t m_sz = 0;
206
207 if (!adp || !io || !ua) {
208 BCMLOG_ERR("Invalid Arg!!\n");
209 return -EINVAL;
210 }
211
Aybuke Ozdemir80b23922014-03-14 23:44:54 +0200212 rc = crystalhd_user_data((void __user *)ua, &io->udata,
213 sizeof(io->udata), set);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500214 if (rc) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100215 BCMLOG_ERR("failed to %s iodata\n", (set ? "set" : "get"));
Jarod Wilson7963eb42010-01-04 18:02:27 -0500216 return rc;
217 }
218
219 switch (io->cmd) {
220 case BCM_IOC_MEM_RD:
221 case BCM_IOC_MEM_WR:
222 case BCM_IOC_FW_DOWNLOAD:
223 m_sz = io->udata.u.devMem.NumDwords * 4;
224 if (set)
225 rc = chd_dec_release_cdata(adp, io, ua);
226 else
227 rc = chd_dec_fetch_cdata(adp, io, m_sz, ua);
228 break;
229 default:
230 break;
231 }
232
233 return rc;
234}
235
236static int chd_dec_api_cmd(struct crystalhd_adp *adp, unsigned long ua,
237 uint32_t uid, uint32_t cmd, crystalhd_cmd_proc func)
238{
239 int rc;
Lior Dotanabfc7682010-05-18 12:46:42 +0300240 struct crystalhd_ioctl_data *temp;
241 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500242
243 temp = chd_dec_alloc_iodata(adp, 0);
244 if (!temp) {
245 BCMLOG_ERR("Failed to get iodata..\n");
246 return -EINVAL;
247 }
248
249 temp->u_id = uid;
250 temp->cmd = cmd;
251
252 rc = chd_dec_proc_user_data(adp, temp, ua, 0);
253 if (!rc) {
254 sts = func(&adp->cmds, temp);
255 if (sts == BC_STS_PENDING)
256 sts = BC_STS_NOT_IMPL;
257 temp->udata.RetSts = sts;
258 rc = chd_dec_proc_user_data(adp, temp, ua, 1);
259 }
260
Dan Carpenter2a1afdb2013-05-30 11:00:34 +0300261 chd_dec_free_iodata(adp, temp, 0);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500262
263 return rc;
264}
265
Ameya Palande21b08382010-02-24 20:18:28 +0200266/* API interfaces */
Arnd Bergmannb1f2ac02010-04-27 20:15:07 +0200267static long chd_dec_ioctl(struct file *fd, unsigned int cmd, unsigned long ua)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500268{
269 struct crystalhd_adp *adp = chd_get_adp();
270 crystalhd_cmd_proc cproc;
271 struct crystalhd_user *uc;
Arnd Bergmannb1f2ac02010-04-27 20:15:07 +0200272 int ret;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500273
274 if (!adp || !fd) {
275 BCMLOG_ERR("Invalid adp\n");
276 return -EINVAL;
277 }
278
Kulikov Vasiliy764f2ca2010-06-29 14:15:43 +0400279 uc = fd->private_data;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500280 if (!uc) {
281 BCMLOG_ERR("Failed to get uc\n");
282 return -ENODATA;
283 }
284
Arnd Bergmann8e2394a2010-07-11 23:18:52 +0200285 mutex_lock(&chd_dec_mutex);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500286 cproc = crystalhd_get_cmd_proc(&adp->cmds, cmd, uc);
287 if (!cproc) {
288 BCMLOG_ERR("Unhandled command: %d\n", cmd);
Arnd Bergmann8e2394a2010-07-11 23:18:52 +0200289 mutex_unlock(&chd_dec_mutex);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500290 return -EINVAL;
291 }
292
Arnd Bergmannb1f2ac02010-04-27 20:15:07 +0200293 ret = chd_dec_api_cmd(adp, ua, uc->uid, cmd, cproc);
Arnd Bergmann8e2394a2010-07-11 23:18:52 +0200294 mutex_unlock(&chd_dec_mutex);
Arnd Bergmannb1f2ac02010-04-27 20:15:07 +0200295 return ret;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500296}
297
298static int chd_dec_open(struct inode *in, struct file *fd)
299{
300 struct crystalhd_adp *adp = chd_get_adp();
301 int rc = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +0300302 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500303 struct crystalhd_user *uc = NULL;
304
Jarod Wilson7963eb42010-01-04 18:02:27 -0500305 if (!adp) {
306 BCMLOG_ERR("Invalid adp\n");
307 return -EINVAL;
308 }
309
310 if (adp->cfg_users >= BC_LINK_MAX_OPENS) {
311 BCMLOG(BCMLOG_INFO, "Already in use.%d\n", adp->cfg_users);
312 return -EBUSY;
313 }
314
315 sts = crystalhd_user_open(&adp->cmds, &uc);
316 if (sts != BC_STS_SUCCESS) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100317 BCMLOG_ERR("cmd_user_open - %d\n", sts);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500318 rc = -EBUSY;
319 }
320
321 adp->cfg_users++;
322
323 fd->private_data = uc;
324
325 return rc;
326}
327
328static int chd_dec_close(struct inode *in, struct file *fd)
329{
330 struct crystalhd_adp *adp = chd_get_adp();
331 struct crystalhd_user *uc;
332
Jarod Wilson7963eb42010-01-04 18:02:27 -0500333 if (!adp) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100334 BCMLOG_ERR("Invalid adp\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500335 return -EINVAL;
336 }
337
Kulikov Vasiliy764f2ca2010-06-29 14:15:43 +0400338 uc = fd->private_data;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500339 if (!uc) {
340 BCMLOG_ERR("Failed to get uc\n");
341 return -ENODATA;
342 }
343
344 crystalhd_user_close(&adp->cmds, uc);
345
346 adp->cfg_users--;
347
348 return 0;
349}
350
351static const struct file_operations chd_dec_fops = {
352 .owner = THIS_MODULE,
Arnd Bergmannb1f2ac02010-04-27 20:15:07 +0200353 .unlocked_ioctl = chd_dec_ioctl,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500354 .open = chd_dec_open,
355 .release = chd_dec_close,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200356 .llseek = noop_llseek,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500357};
358
Bill Pemberton349affa2012-11-19 13:22:10 -0500359static int chd_dec_init_chdev(struct crystalhd_adp *adp)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500360{
Lior Dotanabfc7682010-05-18 12:46:42 +0300361 struct crystalhd_ioctl_data *temp;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500362 struct device *dev;
363 int rc = -ENODEV, i = 0;
364
365 if (!adp)
366 goto fail;
367
368 adp->chd_dec_major = register_chrdev(0, CRYSTALHD_API_NAME,
369 &chd_dec_fops);
370 if (adp->chd_dec_major < 0) {
371 BCMLOG_ERR("Failed to create config dev\n");
372 rc = adp->chd_dec_major;
373 goto fail;
374 }
375
376 /* register crystalhd class */
377 crystalhd_class = class_create(THIS_MODULE, "crystalhd");
378 if (IS_ERR(crystalhd_class)) {
Devendra Naga3b2f1fb2012-08-06 02:58:47 +0545379 rc = PTR_ERR(crystalhd_class);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500380 BCMLOG_ERR("failed to create class\n");
Devendra Naga675fe092012-08-06 02:59:56 +0545381 goto class_create_fail;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500382 }
383
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700384 dev = device_create(crystalhd_class, NULL,
385 MKDEV(adp->chd_dec_major, 0), NULL, "crystalhd");
Jani Nikula15df6382010-03-11 18:22:51 +0200386 if (IS_ERR(dev)) {
Julia Lawall45145f92012-08-25 21:57:08 +0200387 rc = PTR_ERR(dev);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500388 BCMLOG_ERR("failed to create device\n");
389 goto device_create_fail;
390 }
391
392 rc = crystalhd_create_elem_pool(adp, BC_LINK_ELEM_POOL_SZ);
393 if (rc) {
394 BCMLOG_ERR("failed to create device\n");
395 goto elem_pool_fail;
396 }
397
398 /* Allocate general purpose ioctl pool. */
399 for (i = 0; i < CHD_IODATA_POOL_SZ; i++) {
Amarjargal Gundjalam9ebee9d2013-05-12 20:43:09 -0700400 temp = kzalloc(sizeof(struct crystalhd_ioctl_data),
401 GFP_KERNEL);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500402 if (!temp) {
403 BCMLOG_ERR("ioctl data pool kzalloc failed\n");
404 rc = -ENOMEM;
405 goto kzalloc_fail;
406 }
407 /* Add to global pool.. */
408 chd_dec_free_iodata(adp, temp, 0);
409 }
410
411 return 0;
412
413kzalloc_fail:
414 crystalhd_delete_elem_pool(adp);
415elem_pool_fail:
416 device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
417device_create_fail:
418 class_destroy(crystalhd_class);
Devendra Naga675fe092012-08-06 02:59:56 +0545419class_create_fail:
420 unregister_chrdev(adp->chd_dec_major, CRYSTALHD_API_NAME);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500421fail:
422 return rc;
423}
424
Bill Pembertonfdfa2332012-11-19 13:26:56 -0500425static void chd_dec_release_chdev(struct crystalhd_adp *adp)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500426{
Lior Dotanabfc7682010-05-18 12:46:42 +0300427 struct crystalhd_ioctl_data *temp = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500428 if (!adp)
429 return;
430
431 if (adp->chd_dec_major > 0) {
432 /* unregister crystalhd class */
433 device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
434 unregister_chrdev(adp->chd_dec_major, CRYSTALHD_API_NAME);
435 BCMLOG(BCMLOG_INFO, "released api device - %d\n",
436 adp->chd_dec_major);
437 class_destroy(crystalhd_class);
438 }
439 adp->chd_dec_major = 0;
440
441 /* Clear iodata pool.. */
442 do {
443 temp = chd_dec_alloc_iodata(adp, 0);
Scott Kidder61679442010-05-26 23:38:45 -0500444 kfree(temp);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500445 } while (temp);
446
447 crystalhd_delete_elem_pool(adp);
448}
449
Bill Pemberton349affa2012-11-19 13:22:10 -0500450static int chd_pci_reserve_mem(struct crystalhd_adp *pinfo)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500451{
452 int rc;
453 unsigned long bar2 = pci_resource_start(pinfo->pdev, 2);
454 uint32_t mem_len = pci_resource_len(pinfo->pdev, 2);
455 unsigned long bar0 = pci_resource_start(pinfo->pdev, 0);
456 uint32_t i2o_len = pci_resource_len(pinfo->pdev, 0);
457
458 BCMLOG(BCMLOG_SSTEP, "bar2:0x%lx-0x%08x bar0:0x%lx-0x%08x\n",
459 bar2, mem_len, bar0, i2o_len);
460
461 rc = check_mem_region(bar2, mem_len);
462 if (rc) {
463 BCMLOG_ERR("No valid mem region...\n");
464 return -ENOMEM;
465 }
466
467 pinfo->addr = ioremap_nocache(bar2, mem_len);
468 if (!pinfo->addr) {
469 BCMLOG_ERR("Failed to remap mem region...\n");
470 return -ENOMEM;
471 }
472
473 pinfo->pci_mem_start = bar2;
474 pinfo->pci_mem_len = mem_len;
475
476 rc = check_mem_region(bar0, i2o_len);
477 if (rc) {
478 BCMLOG_ERR("No valid mem region...\n");
479 return -ENOMEM;
480 }
481
482 pinfo->i2o_addr = ioremap_nocache(bar0, i2o_len);
483 if (!pinfo->i2o_addr) {
484 BCMLOG_ERR("Failed to remap mem region...\n");
485 return -ENOMEM;
486 }
487
488 pinfo->pci_i2o_start = bar0;
489 pinfo->pci_i2o_len = i2o_len;
490
491 rc = pci_request_regions(pinfo->pdev, pinfo->name);
492 if (rc < 0) {
493 BCMLOG_ERR("Region request failed: %d\n", rc);
494 return rc;
495 }
496
497 BCMLOG(BCMLOG_SSTEP, "Mapped addr:0x%08lx i2o_addr:0x%08lx\n",
498 (unsigned long)pinfo->addr, (unsigned long)pinfo->i2o_addr);
499
500 return 0;
501}
502
Bill Pembertonfdfa2332012-11-19 13:26:56 -0500503static void chd_pci_release_mem(struct crystalhd_adp *pinfo)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500504{
505 if (!pinfo)
506 return;
507
508 if (pinfo->addr)
509 iounmap(pinfo->addr);
510
511 if (pinfo->i2o_addr)
512 iounmap(pinfo->i2o_addr);
513
514 pci_release_regions(pinfo->pdev);
515}
516
517
Bill Pembertonfdfa2332012-11-19 13:26:56 -0500518static void chd_dec_pci_remove(struct pci_dev *pdev)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500519{
520 struct crystalhd_adp *pinfo;
Lior Dotanabfc7682010-05-18 12:46:42 +0300521 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500522
Joe Perches345594d2010-11-15 12:14:00 -0800523 pinfo = pci_get_drvdata(pdev);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500524 if (!pinfo) {
525 BCMLOG_ERR("could not get adp\n");
526 return;
527 }
528
529 sts = crystalhd_delete_cmd_context(&pinfo->cmds);
530 if (sts != BC_STS_SUCCESS)
Lars Lindley641b63f2010-03-11 00:21:20 +0100531 BCMLOG_ERR("cmd delete :%d\n", sts);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500532
533 chd_dec_release_chdev(pinfo);
534
535 chd_dec_disable_int(pinfo);
536
537 chd_pci_release_mem(pinfo);
538 pci_disable_device(pinfo->pdev);
539
540 kfree(pinfo);
541 g_adp_info = NULL;
542}
543
Bill Pemberton349affa2012-11-19 13:22:10 -0500544static int chd_dec_pci_probe(struct pci_dev *pdev,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500545 const struct pci_device_id *entry)
546{
547 struct crystalhd_adp *pinfo;
548 int rc;
Lior Dotanabfc7682010-05-18 12:46:42 +0300549 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500550
Aybuke Ozdemir80b23922014-03-14 23:44:54 +0200551 BCMLOG(BCMLOG_DBG,
552 "PCI_INFO: Vendor:0x%04x Device:0x%04x s_vendor:0x%04x s_device: 0x%04x\n",
553 pdev->vendor, pdev->device, pdev->subsystem_vendor,
554 pdev->subsystem_device);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500555
Dan Carpentera3b6ff02011-03-14 12:33:37 +0300556 pinfo = kzalloc(sizeof(struct crystalhd_adp), GFP_KERNEL);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500557 if (!pinfo) {
558 BCMLOG_ERR("Failed to allocate memory\n");
559 return -ENOMEM;
560 }
561
562 pinfo->pdev = pdev;
563
564 rc = pci_enable_device(pdev);
565 if (rc) {
566 BCMLOG_ERR("Failed to enable PCI device\n");
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300567 goto err;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500568 }
569
Dan Carpentere2789132011-03-14 12:35:31 +0300570 snprintf(pinfo->name, sizeof(pinfo->name), "crystalhd_pci_e:%d:%d:%d",
Jarod Wilson7963eb42010-01-04 18:02:27 -0500571 pdev->bus->number, PCI_SLOT(pdev->devfn),
572 PCI_FUNC(pdev->devfn));
573
574 rc = chd_pci_reserve_mem(pinfo);
575 if (rc) {
576 BCMLOG_ERR("Failed to setup memory regions.\n");
Kulikov Vasiliy036b00e2010-08-09 23:51:44 +0400577 pci_disable_device(pdev);
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300578 rc = -ENOMEM;
579 goto err;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500580 }
581
582 pinfo->present = 1;
583 pinfo->drv_data = entry->driver_data;
584
585 /* Setup adapter level lock.. */
586 spin_lock_init(&pinfo->lock);
587
588 /* setup api stuff.. */
589 chd_dec_init_chdev(pinfo);
590 rc = chd_dec_enable_int(pinfo);
591 if (rc) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100592 BCMLOG_ERR("_enable_int err:%d\n", rc);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500593 pci_disable_device(pdev);
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300594 rc = -ENODEV;
595 goto err;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500596 }
597
598 /* Set dma mask... */
599 if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
600 pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
601 pinfo->dmabits = 64;
602 } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
603 pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
604 pinfo->dmabits = 32;
605 } else {
606 BCMLOG_ERR("Unabled to setup DMA %d\n", rc);
607 pci_disable_device(pdev);
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300608 rc = -ENODEV;
609 goto err;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500610 }
611
612 sts = crystalhd_setup_cmd_context(&pinfo->cmds, pinfo);
613 if (sts != BC_STS_SUCCESS) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100614 BCMLOG_ERR("cmd setup :%d\n", sts);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500615 pci_disable_device(pdev);
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300616 rc = -ENODEV;
617 goto err;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500618 }
619
620 pci_set_master(pdev);
621
622 pci_set_drvdata(pdev, pinfo);
623
624 g_adp_info = pinfo;
625
626 return 0;
Alexander Beregalovb4c77842011-03-13 21:58:48 +0300627
628err:
629 kfree(pinfo);
630 return rc;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500631}
632
633#ifdef CONFIG_PM
Valentina Manea038d3f82013-10-07 23:12:14 +0300634static int chd_dec_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500635{
636 struct crystalhd_adp *adp;
Lior Dotanabfc7682010-05-18 12:46:42 +0300637 struct crystalhd_ioctl_data *temp;
638 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500639
Joe Perches345594d2010-11-15 12:14:00 -0800640 adp = pci_get_drvdata(pdev);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500641 if (!adp) {
642 BCMLOG_ERR("could not get adp\n");
643 return -ENODEV;
644 }
645
646 temp = chd_dec_alloc_iodata(adp, false);
647 if (!temp) {
648 BCMLOG_ERR("could not get ioctl data\n");
649 return -ENODEV;
650 }
651
652 sts = crystalhd_suspend(&adp->cmds, temp);
653 if (sts != BC_STS_SUCCESS) {
654 BCMLOG_ERR("BCM70012 Suspend %d\n", sts);
655 return -ENODEV;
656 }
657
658 chd_dec_free_iodata(adp, temp, false);
659 chd_dec_disable_int(adp);
660 pci_save_state(pdev);
661
662 /* Disable IO/bus master/irq router */
663 pci_disable_device(pdev);
664 pci_set_power_state(pdev, pci_choose_state(pdev, state));
665 return 0;
666}
667
Valentina Manea038d3f82013-10-07 23:12:14 +0300668static int chd_dec_pci_resume(struct pci_dev *pdev)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500669{
670 struct crystalhd_adp *adp;
Lior Dotanabfc7682010-05-18 12:46:42 +0300671 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500672 int rc;
673
Joe Perches345594d2010-11-15 12:14:00 -0800674 adp = pci_get_drvdata(pdev);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500675 if (!adp) {
676 BCMLOG_ERR("could not get adp\n");
677 return -ENODEV;
678 }
679
680 pci_set_power_state(pdev, PCI_D0);
681 pci_restore_state(pdev);
682
683 /* device's irq possibly is changed, driver should take care */
684 if (pci_enable_device(pdev)) {
685 BCMLOG_ERR("Failed to enable PCI device\n");
686 return 1;
687 }
688
689 pci_set_master(pdev);
690
691 rc = chd_dec_enable_int(adp);
692 if (rc) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100693 BCMLOG_ERR("_enable_int err:%d\n", rc);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500694 pci_disable_device(pdev);
695 return -ENODEV;
696 }
697
698 sts = crystalhd_resume(&adp->cmds);
699 if (sts != BC_STS_SUCCESS) {
700 BCMLOG_ERR("BCM70012 Resume %d\n", sts);
701 pci_disable_device(pdev);
702 return -ENODEV;
703 }
704
705 return 0;
706}
707#endif
708
Jingoo Han41e043f2013-12-03 08:26:00 +0900709static const struct pci_device_id chd_dec_pci_id_table[] = {
Ameya Palande21b08382010-02-24 20:18:28 +0200710 { PCI_VDEVICE(BROADCOM, 0x1612), 8 },
Jarod Wilson7963eb42010-01-04 18:02:27 -0500711 { 0, },
712};
Ameya Palande21b08382010-02-24 20:18:28 +0200713MODULE_DEVICE_TABLE(pci, chd_dec_pci_id_table);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500714
Ameya Palande21b08382010-02-24 20:18:28 +0200715static struct pci_driver bc_chd_70012_driver = {
Jarod Wilson7963eb42010-01-04 18:02:27 -0500716 .name = "Broadcom 70012 Decoder",
717 .probe = chd_dec_pci_probe,
Bill Pemberton417696c2012-11-19 13:20:49 -0500718 .remove = chd_dec_pci_remove,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500719 .id_table = chd_dec_pci_id_table,
720#ifdef CONFIG_PM
721 .suspend = chd_dec_pci_suspend,
722 .resume = chd_dec_pci_resume
723#endif
724};
Jarod Wilson7963eb42010-01-04 18:02:27 -0500725
726void chd_set_log_level(struct crystalhd_adp *adp, char *arg)
727{
728 if ((!arg) || (strlen(arg) < 3))
729 g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA;
730 else if (!strncmp(arg, "sstep", 5))
731 g_linklog_level = BCMLOG_INFO | BCMLOG_DATA | BCMLOG_DBG |
732 BCMLOG_SSTEP | BCMLOG_ERROR;
733 else if (!strncmp(arg, "info", 4))
734 g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO;
735 else if (!strncmp(arg, "debug", 5))
736 g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO |
737 BCMLOG_DBG;
738 else if (!strncmp(arg, "pball", 5))
739 g_linklog_level = 0xFFFFFFFF & ~(BCMLOG_SPINLOCK);
740 else if (!strncmp(arg, "silent", 6))
741 g_linklog_level = 0;
742 else
743 g_linklog_level = 0;
744}
745
746struct crystalhd_adp *chd_get_adp(void)
747{
748 return g_adp_info;
749}
750
Ameya Palande21b08382010-02-24 20:18:28 +0200751static int __init chd_dec_module_init(void)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500752{
753 int rc;
754
755 chd_set_log_level(NULL, "debug");
Lars Lindley641b63f2010-03-11 00:21:20 +0100756 BCMLOG(BCMLOG_DATA, "Loading crystalhd %d.%d.%d\n",
Jarod Wilson7963eb42010-01-04 18:02:27 -0500757 crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
758
759 rc = pci_register_driver(&bc_chd_70012_driver);
760
761 if (rc < 0)
Lars Lindley641b63f2010-03-11 00:21:20 +0100762 BCMLOG_ERR("Could not find any devices. err:%d\n", rc);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500763
764 return rc;
765}
Ameya Palande21b08382010-02-24 20:18:28 +0200766module_init(chd_dec_module_init);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500767
Ameya Palande21b08382010-02-24 20:18:28 +0200768static void __exit chd_dec_module_cleanup(void)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500769{
Lars Lindley641b63f2010-03-11 00:21:20 +0100770 BCMLOG(BCMLOG_DATA, "unloading crystalhd %d.%d.%d\n",
Jarod Wilson7963eb42010-01-04 18:02:27 -0500771 crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
772
773 pci_unregister_driver(&bc_chd_70012_driver);
774}
Ameya Palande21b08382010-02-24 20:18:28 +0200775module_exit(chd_dec_module_cleanup);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500776
777MODULE_AUTHOR("Naren Sankar <nsankar@broadcom.com>");
778MODULE_AUTHOR("Prasad Bolisetty <prasadb@broadcom.com>");
779MODULE_DESCRIPTION(CRYSTAL_HD_NAME);
780MODULE_LICENSE("GPL");
781MODULE_ALIAS("bcm70012");