Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.2 356 lines 8.1 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Wireless Host Controller (WHC) driver. 4 * 5 * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 6 */ 7#include <linux/kernel.h> 8#include <linux/init.h> 9#include <linux/module.h> 10#include <linux/uwb/umc.h> 11 12#include "../../wusbcore/wusbhc.h" 13 14#include "whcd.h" 15 16/* 17 * One time initialization. 18 * 19 * Nothing to do here. 20 */ 21static int whc_reset(struct usb_hcd *usb_hcd) 22{ 23 return 0; 24} 25 26/* 27 * Start the wireless host controller. 28 * 29 * Start device notification. 30 * 31 * Put hc into run state, set DNTS parameters. 32 */ 33static int whc_start(struct usb_hcd *usb_hcd) 34{ 35 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 36 struct whc *whc = wusbhc_to_whc(wusbhc); 37 u8 bcid; 38 int ret; 39 40 mutex_lock(&wusbhc->mutex); 41 42 le_writel(WUSBINTR_GEN_CMD_DONE 43 | WUSBINTR_HOST_ERR 44 | WUSBINTR_ASYNC_SCHED_SYNCED 45 | WUSBINTR_DNTS_INT 46 | WUSBINTR_ERR_INT 47 | WUSBINTR_INT, 48 whc->base + WUSBINTR); 49 50 /* set cluster ID */ 51 bcid = wusb_cluster_id_get(); 52 ret = whc_set_cluster_id(whc, bcid); 53 if (ret < 0) 54 goto out; 55 wusbhc->cluster_id = bcid; 56 57 /* start HC */ 58 whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); 59 60 usb_hcd->uses_new_polling = 1; 61 set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); 62 usb_hcd->state = HC_STATE_RUNNING; 63 64out: 65 mutex_unlock(&wusbhc->mutex); 66 return ret; 67} 68 69 70/* 71 * Stop the wireless host controller. 72 * 73 * Stop device notification. 74 * 75 * Wait for pending transfer to stop? Put hc into stop state? 76 */ 77static void whc_stop(struct usb_hcd *usb_hcd) 78{ 79 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 80 struct whc *whc = wusbhc_to_whc(wusbhc); 81 82 mutex_lock(&wusbhc->mutex); 83 84 /* stop HC */ 85 le_writel(0, whc->base + WUSBINTR); 86 whc_write_wusbcmd(whc, WUSBCMD_RUN, 0); 87 whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, 88 WUSBSTS_HCHALTED, WUSBSTS_HCHALTED, 89 100, "HC to halt"); 90 91 wusb_cluster_id_put(wusbhc->cluster_id); 92 93 mutex_unlock(&wusbhc->mutex); 94} 95 96static int whc_get_frame_number(struct usb_hcd *usb_hcd) 97{ 98 /* Frame numbers are not applicable to WUSB. */ 99 return -ENOSYS; 100} 101 102 103/* 104 * Queue an URB to the ASL or PZL 105 */ 106static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, 107 gfp_t mem_flags) 108{ 109 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 110 struct whc *whc = wusbhc_to_whc(wusbhc); 111 int ret; 112 113 switch (usb_pipetype(urb->pipe)) { 114 case PIPE_INTERRUPT: 115 ret = pzl_urb_enqueue(whc, urb, mem_flags); 116 break; 117 case PIPE_ISOCHRONOUS: 118 dev_err(&whc->umc->dev, "isochronous transfers unsupported\n"); 119 ret = -ENOTSUPP; 120 break; 121 case PIPE_CONTROL: 122 case PIPE_BULK: 123 default: 124 ret = asl_urb_enqueue(whc, urb, mem_flags); 125 break; 126 } 127 128 return ret; 129} 130 131/* 132 * Remove a queued URB from the ASL or PZL. 133 */ 134static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) 135{ 136 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 137 struct whc *whc = wusbhc_to_whc(wusbhc); 138 int ret; 139 140 switch (usb_pipetype(urb->pipe)) { 141 case PIPE_INTERRUPT: 142 ret = pzl_urb_dequeue(whc, urb, status); 143 break; 144 case PIPE_ISOCHRONOUS: 145 ret = -ENOTSUPP; 146 break; 147 case PIPE_CONTROL: 148 case PIPE_BULK: 149 default: 150 ret = asl_urb_dequeue(whc, urb, status); 151 break; 152 } 153 154 return ret; 155} 156 157/* 158 * Wait for all URBs to the endpoint to be completed, then delete the 159 * qset. 160 */ 161static void whc_endpoint_disable(struct usb_hcd *usb_hcd, 162 struct usb_host_endpoint *ep) 163{ 164 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 165 struct whc *whc = wusbhc_to_whc(wusbhc); 166 struct whc_qset *qset; 167 168 qset = ep->hcpriv; 169 if (qset) { 170 ep->hcpriv = NULL; 171 if (usb_endpoint_xfer_bulk(&ep->desc) 172 || usb_endpoint_xfer_control(&ep->desc)) 173 asl_qset_delete(whc, qset); 174 else 175 pzl_qset_delete(whc, qset); 176 } 177} 178 179static void whc_endpoint_reset(struct usb_hcd *usb_hcd, 180 struct usb_host_endpoint *ep) 181{ 182 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 183 struct whc *whc = wusbhc_to_whc(wusbhc); 184 struct whc_qset *qset; 185 unsigned long flags; 186 187 spin_lock_irqsave(&whc->lock, flags); 188 189 qset = ep->hcpriv; 190 if (qset) { 191 qset->remove = 1; 192 qset->reset = 1; 193 194 if (usb_endpoint_xfer_bulk(&ep->desc) 195 || usb_endpoint_xfer_control(&ep->desc)) 196 queue_work(whc->workqueue, &whc->async_work); 197 else 198 queue_work(whc->workqueue, &whc->periodic_work); 199 } 200 201 spin_unlock_irqrestore(&whc->lock, flags); 202} 203 204 205static const struct hc_driver whc_hc_driver = { 206 .description = "whci-hcd", 207 .product_desc = "Wireless host controller", 208 .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd), 209 .irq = whc_int_handler, 210 .flags = HCD_USB2, 211 212 .reset = whc_reset, 213 .start = whc_start, 214 .stop = whc_stop, 215 .get_frame_number = whc_get_frame_number, 216 .urb_enqueue = whc_urb_enqueue, 217 .urb_dequeue = whc_urb_dequeue, 218 .endpoint_disable = whc_endpoint_disable, 219 .endpoint_reset = whc_endpoint_reset, 220 221 .hub_status_data = wusbhc_rh_status_data, 222 .hub_control = wusbhc_rh_control, 223 .start_port_reset = wusbhc_rh_start_port_reset, 224}; 225 226static int whc_probe(struct umc_dev *umc) 227{ 228 int ret; 229 struct usb_hcd *usb_hcd; 230 struct wusbhc *wusbhc; 231 struct whc *whc; 232 struct device *dev = &umc->dev; 233 234 usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci"); 235 if (usb_hcd == NULL) { 236 dev_err(dev, "unable to create hcd\n"); 237 return -ENOMEM; 238 } 239 240 usb_hcd->wireless = 1; 241 usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ 242 243 wusbhc = usb_hcd_to_wusbhc(usb_hcd); 244 whc = wusbhc_to_whc(wusbhc); 245 whc->umc = umc; 246 247 ret = whc_init(whc); 248 if (ret) 249 goto error_whc_init; 250 251 wusbhc->dev = dev; 252 wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent); 253 if (!wusbhc->uwb_rc) { 254 ret = -ENODEV; 255 dev_err(dev, "cannot get radio controller\n"); 256 goto error_uwb_rc; 257 } 258 259 if (whc->n_devices > USB_MAXCHILDREN) { 260 dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n", 261 whc->n_devices); 262 wusbhc->ports_max = USB_MAXCHILDREN; 263 } else 264 wusbhc->ports_max = whc->n_devices; 265 wusbhc->mmcies_max = whc->n_mmc_ies; 266 wusbhc->start = whc_wusbhc_start; 267 wusbhc->stop = whc_wusbhc_stop; 268 wusbhc->mmcie_add = whc_mmcie_add; 269 wusbhc->mmcie_rm = whc_mmcie_rm; 270 wusbhc->dev_info_set = whc_dev_info_set; 271 wusbhc->bwa_set = whc_bwa_set; 272 wusbhc->set_num_dnts = whc_set_num_dnts; 273 wusbhc->set_ptk = whc_set_ptk; 274 wusbhc->set_gtk = whc_set_gtk; 275 276 ret = wusbhc_create(wusbhc); 277 if (ret) 278 goto error_wusbhc_create; 279 280 ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED); 281 if (ret) { 282 dev_err(dev, "cannot add HCD: %d\n", ret); 283 goto error_usb_add_hcd; 284 } 285 device_wakeup_enable(usb_hcd->self.controller); 286 287 ret = wusbhc_b_create(wusbhc); 288 if (ret) { 289 dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret); 290 goto error_wusbhc_b_create; 291 } 292 293 whc_dbg_init(whc); 294 295 return 0; 296 297error_wusbhc_b_create: 298 usb_remove_hcd(usb_hcd); 299error_usb_add_hcd: 300 wusbhc_destroy(wusbhc); 301error_wusbhc_create: 302 uwb_rc_put(wusbhc->uwb_rc); 303error_uwb_rc: 304 whc_clean_up(whc); 305error_whc_init: 306 usb_put_hcd(usb_hcd); 307 return ret; 308} 309 310 311static void whc_remove(struct umc_dev *umc) 312{ 313 struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev); 314 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 315 struct whc *whc = wusbhc_to_whc(wusbhc); 316 317 if (usb_hcd) { 318 whc_dbg_clean_up(whc); 319 wusbhc_b_destroy(wusbhc); 320 usb_remove_hcd(usb_hcd); 321 wusbhc_destroy(wusbhc); 322 uwb_rc_put(wusbhc->uwb_rc); 323 whc_clean_up(whc); 324 usb_put_hcd(usb_hcd); 325 } 326} 327 328static struct umc_driver whci_hc_driver = { 329 .name = "whci-hcd", 330 .cap_id = UMC_CAP_ID_WHCI_WUSB_HC, 331 .probe = whc_probe, 332 .remove = whc_remove, 333}; 334 335static int __init whci_hc_driver_init(void) 336{ 337 return umc_driver_register(&whci_hc_driver); 338} 339module_init(whci_hc_driver_init); 340 341static void __exit whci_hc_driver_exit(void) 342{ 343 umc_driver_unregister(&whci_hc_driver); 344} 345module_exit(whci_hc_driver_exit); 346 347/* PCI device ID's that we handle (so it gets loaded) */ 348static struct pci_device_id __used whci_hcd_id_table[] = { 349 { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, 350 { /* empty last entry */ } 351}; 352MODULE_DEVICE_TABLE(pci, whci_hcd_id_table); 353 354MODULE_DESCRIPTION("WHCI Wireless USB host controller driver"); 355MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); 356MODULE_LICENSE("GPL");