Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / staging / wusbcore / host / whci / init.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Wireless Host Controller (WHC) initialization.
4  *
5  * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
6  */
7 #include <linux/kernel.h>
8 #include <linux/gfp.h>
9 #include <linux/dma-mapping.h>
10
11 #include "../../../uwb/include/umc.h"
12 #include "../../wusbhc.h"
13
14 #include "whcd.h"
15
16 /*
17  * Reset the host controller.
18  */
19 static void whc_hw_reset(struct whc *whc)
20 {
21         le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
22         whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
23                       100, "reset");
24 }
25
26 static void whc_hw_init_di_buf(struct whc *whc)
27 {
28         int d;
29
30         /* Disable all entries in the Device Information buffer. */
31         for (d = 0; d < whc->n_devices; d++)
32                 whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
33
34         le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
35 }
36
37 static void whc_hw_init_dn_buf(struct whc *whc)
38 {
39         /* Clear the Device Notification buffer to ensure the V (valid)
40          * bits are clear.  */
41         memset(whc->dn_buf, 0, 4096);
42
43         le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
44 }
45
46 int whc_init(struct whc *whc)
47 {
48         u32 whcsparams;
49         int ret, i;
50         resource_size_t start, len;
51
52         spin_lock_init(&whc->lock);
53         mutex_init(&whc->mutex);
54         init_waitqueue_head(&whc->cmd_wq);
55         init_waitqueue_head(&whc->async_list_wq);
56         init_waitqueue_head(&whc->periodic_list_wq);
57         whc->workqueue = alloc_ordered_workqueue(dev_name(&whc->umc->dev), 0);
58         if (whc->workqueue == NULL) {
59                 ret = -ENOMEM;
60                 goto error;
61         }
62         INIT_WORK(&whc->dn_work, whc_dn_work);
63
64         INIT_WORK(&whc->async_work, scan_async_work);
65         INIT_LIST_HEAD(&whc->async_list);
66         INIT_LIST_HEAD(&whc->async_removed_list);
67
68         INIT_WORK(&whc->periodic_work, scan_periodic_work);
69         for (i = 0; i < 5; i++)
70                 INIT_LIST_HEAD(&whc->periodic_list[i]);
71         INIT_LIST_HEAD(&whc->periodic_removed_list);
72
73         /* Map HC registers. */
74         start = whc->umc->resource.start;
75         len   = whc->umc->resource.end - start + 1;
76         if (!request_mem_region(start, len, "whci-hc")) {
77                 dev_err(&whc->umc->dev, "can't request HC region\n");
78                 ret = -EBUSY;
79                 goto error;
80         }
81         whc->base_phys = start;
82         whc->base = ioremap(start, len);
83         if (!whc->base) {
84                 dev_err(&whc->umc->dev, "ioremap\n");
85                 ret = -ENOMEM;
86                 goto error;
87         }
88
89         whc_hw_reset(whc);
90
91         /* Read maximum number of devices, keys and MMC IEs. */
92         whcsparams = le_readl(whc->base + WHCSPARAMS);
93         whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
94         whc->n_keys    = WHCSPARAMS_TO_N_KEYS(whcsparams);
95         whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
96
97         dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
98                 whc->n_devices, whc->n_keys, whc->n_mmc_ies);
99
100         whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
101                                          sizeof(struct whc_qset), 64, 0);
102         if (whc->qset_pool == NULL) {
103                 ret = -ENOMEM;
104                 goto error;
105         }
106
107         ret = asl_init(whc);
108         if (ret < 0)
109                 goto error;
110         ret = pzl_init(whc);
111         if (ret < 0)
112                 goto error;
113
114         /* Allocate and initialize a buffer for generic commands, the
115            Device Information buffer, and the Device Notification
116            buffer. */
117
118         whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
119                                               &whc->gen_cmd_buf_dma, GFP_KERNEL);
120         if (whc->gen_cmd_buf == NULL) {
121                 ret = -ENOMEM;
122                 goto error;
123         }
124
125         whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
126                                          sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
127                                          &whc->dn_buf_dma, GFP_KERNEL);
128         if (!whc->dn_buf) {
129                 ret = -ENOMEM;
130                 goto error;
131         }
132         whc_hw_init_dn_buf(whc);
133
134         whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
135                                          sizeof(struct di_buf_entry) * whc->n_devices,
136                                          &whc->di_buf_dma, GFP_KERNEL);
137         if (!whc->di_buf) {
138                 ret = -ENOMEM;
139                 goto error;
140         }
141         whc_hw_init_di_buf(whc);
142
143         return 0;
144
145 error:
146         whc_clean_up(whc);
147         return ret;
148 }
149
150 void whc_clean_up(struct whc *whc)
151 {
152         resource_size_t len;
153
154         if (whc->di_buf)
155                 dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
156                                   whc->di_buf, whc->di_buf_dma);
157         if (whc->dn_buf)
158                 dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
159                                   whc->dn_buf, whc->dn_buf_dma);
160         if (whc->gen_cmd_buf)
161                 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
162                                   whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
163
164         pzl_clean_up(whc);
165         asl_clean_up(whc);
166
167         dma_pool_destroy(whc->qset_pool);
168
169         len   = resource_size(&whc->umc->resource);
170         if (whc->base)
171                 iounmap(whc->base);
172         if (whc->base_phys)
173                 release_mem_region(whc->base_phys, len);
174
175         if (whc->workqueue)
176                 destroy_workqueue(whc->workqueue);
177 }