48a6f50df24ad3685eb138579a4483057beb2f82
[linux-2.6-microblaze.git] / drivers / usb / 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  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version
9  * 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <linux/kernel.h>
20 #include <linux/gfp.h>
21 #include <linux/dma-mapping.h>
22 #include <linux/uwb/umc.h>
23
24 #include "../../wusbcore/wusbhc.h"
25
26 #include "whcd.h"
27
28 /*
29  * Reset the host controller.
30  */
31 static void whc_hw_reset(struct whc *whc)
32 {
33         le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
34         whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
35                       100, "reset");
36 }
37
38 static void whc_hw_init_di_buf(struct whc *whc)
39 {
40         int d;
41
42         /* Disable all entries in the Device Information buffer. */
43         for (d = 0; d < whc->n_devices; d++)
44                 whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
45
46         le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
47 }
48
49 static void whc_hw_init_dn_buf(struct whc *whc)
50 {
51         /* Clear the Device Notification buffer to ensure the V (valid)
52          * bits are clear.  */
53         memset(whc->dn_buf, 0, 4096);
54
55         le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
56 }
57
58 int whc_init(struct whc *whc)
59 {
60         u32 whcsparams;
61         int ret, i;
62         resource_size_t start, len;
63
64         spin_lock_init(&whc->lock);
65         mutex_init(&whc->mutex);
66         init_waitqueue_head(&whc->cmd_wq);
67         init_waitqueue_head(&whc->async_list_wq);
68         init_waitqueue_head(&whc->periodic_list_wq);
69         whc->workqueue = alloc_ordered_workqueue(dev_name(&whc->umc->dev), 0);
70         if (whc->workqueue == NULL) {
71                 ret = -ENOMEM;
72                 goto error;
73         }
74         INIT_WORK(&whc->dn_work, whc_dn_work);
75
76         INIT_WORK(&whc->async_work, scan_async_work);
77         INIT_LIST_HEAD(&whc->async_list);
78         INIT_LIST_HEAD(&whc->async_removed_list);
79
80         INIT_WORK(&whc->periodic_work, scan_periodic_work);
81         for (i = 0; i < 5; i++)
82                 INIT_LIST_HEAD(&whc->periodic_list[i]);
83         INIT_LIST_HEAD(&whc->periodic_removed_list);
84
85         /* Map HC registers. */
86         start = whc->umc->resource.start;
87         len   = whc->umc->resource.end - start + 1;
88         if (!request_mem_region(start, len, "whci-hc")) {
89                 dev_err(&whc->umc->dev, "can't request HC region\n");
90                 ret = -EBUSY;
91                 goto error;
92         }
93         whc->base_phys = start;
94         whc->base = ioremap(start, len);
95         if (!whc->base) {
96                 dev_err(&whc->umc->dev, "ioremap\n");
97                 ret = -ENOMEM;
98                 goto error;
99         }
100
101         whc_hw_reset(whc);
102
103         /* Read maximum number of devices, keys and MMC IEs. */
104         whcsparams = le_readl(whc->base + WHCSPARAMS);
105         whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
106         whc->n_keys    = WHCSPARAMS_TO_N_KEYS(whcsparams);
107         whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
108
109         dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
110                 whc->n_devices, whc->n_keys, whc->n_mmc_ies);
111
112         whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
113                                          sizeof(struct whc_qset), 64, 0);
114         if (whc->qset_pool == NULL) {
115                 ret = -ENOMEM;
116                 goto error;
117         }
118
119         ret = asl_init(whc);
120         if (ret < 0)
121                 goto error;
122         ret = pzl_init(whc);
123         if (ret < 0)
124                 goto error;
125
126         /* Allocate and initialize a buffer for generic commands, the
127            Device Information buffer, and the Device Notification
128            buffer. */
129
130         whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
131                                               &whc->gen_cmd_buf_dma, GFP_KERNEL);
132         if (whc->gen_cmd_buf == NULL) {
133                 ret = -ENOMEM;
134                 goto error;
135         }
136
137         whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
138                                          sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
139                                          &whc->dn_buf_dma, GFP_KERNEL);
140         if (!whc->dn_buf) {
141                 ret = -ENOMEM;
142                 goto error;
143         }
144         whc_hw_init_dn_buf(whc);
145
146         whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
147                                          sizeof(struct di_buf_entry) * whc->n_devices,
148                                          &whc->di_buf_dma, GFP_KERNEL);
149         if (!whc->di_buf) {
150                 ret = -ENOMEM;
151                 goto error;
152         }
153         whc_hw_init_di_buf(whc);
154
155         return 0;
156
157 error:
158         whc_clean_up(whc);
159         return ret;
160 }
161
162 void whc_clean_up(struct whc *whc)
163 {
164         resource_size_t len;
165
166         if (whc->di_buf)
167                 dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
168                                   whc->di_buf, whc->di_buf_dma);
169         if (whc->dn_buf)
170                 dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
171                                   whc->dn_buf, whc->dn_buf_dma);
172         if (whc->gen_cmd_buf)
173                 dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN,
174                                   whc->gen_cmd_buf, whc->gen_cmd_buf_dma);
175
176         pzl_clean_up(whc);
177         asl_clean_up(whc);
178
179         dma_pool_destroy(whc->qset_pool);
180
181         len   = resource_size(&whc->umc->resource);
182         if (whc->base)
183                 iounmap(whc->base);
184         if (whc->base_phys)
185                 release_mem_region(whc->base_phys, len);
186
187         if (whc->workqueue)
188                 destroy_workqueue(whc->workqueue);
189 }