c67x00-drv.c (5144B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * c67x00-drv.c: Cypress C67X00 USB Common infrastructure 4 * 5 * Copyright (C) 2006-2008 Barco N.V. 6 * Derived from the Cypress cy7c67200/300 ezusb linux driver and 7 * based on multiple host controller drivers inside the linux kernel. 8 */ 9 10/* 11 * This file implements the common infrastructure for using the c67x00. 12 * It is both the link between the platform configuration and subdrivers and 13 * the link between the common hardware parts and the subdrivers (e.g. 14 * interrupt handling). 15 * 16 * The c67x00 has 2 SIE's (serial interface engine) which can be configured 17 * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG). 18 * 19 * Depending on the platform configuration, the SIE's are created and 20 * the corresponding subdriver is initialized (c67x00_probe_sie). 21 */ 22 23#include <linux/device.h> 24#include <linux/io.h> 25#include <linux/list.h> 26#include <linux/slab.h> 27#include <linux/module.h> 28#include <linux/usb.h> 29#include <linux/usb/c67x00.h> 30 31#include "c67x00.h" 32#include "c67x00-hcd.h" 33 34static void c67x00_probe_sie(struct c67x00_sie *sie, 35 struct c67x00_device *dev, int sie_num) 36{ 37 spin_lock_init(&sie->lock); 38 sie->dev = dev; 39 sie->sie_num = sie_num; 40 sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num); 41 42 switch (sie->mode) { 43 case C67X00_SIE_HOST: 44 c67x00_hcd_probe(sie); 45 break; 46 47 case C67X00_SIE_UNUSED: 48 dev_info(sie_dev(sie), 49 "Not using SIE %d as requested\n", sie->sie_num); 50 break; 51 52 default: 53 dev_err(sie_dev(sie), 54 "Unsupported configuration: 0x%x for SIE %d\n", 55 sie->mode, sie->sie_num); 56 break; 57 } 58} 59 60static void c67x00_remove_sie(struct c67x00_sie *sie) 61{ 62 switch (sie->mode) { 63 case C67X00_SIE_HOST: 64 c67x00_hcd_remove(sie); 65 break; 66 67 default: 68 break; 69 } 70} 71 72static irqreturn_t c67x00_irq(int irq, void *__dev) 73{ 74 struct c67x00_device *c67x00 = __dev; 75 struct c67x00_sie *sie; 76 u16 msg, int_status; 77 int i, count = 8; 78 79 int_status = c67x00_ll_hpi_status(c67x00); 80 if (!int_status) 81 return IRQ_NONE; 82 83 while (int_status != 0 && (count-- >= 0)) { 84 c67x00_ll_irq(c67x00, int_status); 85 for (i = 0; i < C67X00_SIES; i++) { 86 sie = &c67x00->sie[i]; 87 msg = 0; 88 if (int_status & SIEMSG_FLG(i)) 89 msg = c67x00_ll_fetch_siemsg(c67x00, i); 90 if (sie->irq) 91 sie->irq(sie, int_status, msg); 92 } 93 int_status = c67x00_ll_hpi_status(c67x00); 94 } 95 96 if (int_status) 97 dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! " 98 "status = 0x%04x\n", int_status); 99 100 return IRQ_HANDLED; 101} 102 103/* ------------------------------------------------------------------------- */ 104 105static int c67x00_drv_probe(struct platform_device *pdev) 106{ 107 struct c67x00_device *c67x00; 108 struct c67x00_platform_data *pdata; 109 struct resource *res, *res2; 110 int ret, i; 111 112 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 113 if (!res) 114 return -ENODEV; 115 116 res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 117 if (!res2) 118 return -ENODEV; 119 120 pdata = dev_get_platdata(&pdev->dev); 121 if (!pdata) 122 return -ENODEV; 123 124 c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL); 125 if (!c67x00) 126 return -ENOMEM; 127 128 if (!request_mem_region(res->start, resource_size(res), 129 pdev->name)) { 130 dev_err(&pdev->dev, "Memory region busy\n"); 131 ret = -EBUSY; 132 goto request_mem_failed; 133 } 134 c67x00->hpi.base = ioremap(res->start, resource_size(res)); 135 if (!c67x00->hpi.base) { 136 dev_err(&pdev->dev, "Unable to map HPI registers\n"); 137 ret = -EIO; 138 goto map_failed; 139 } 140 141 spin_lock_init(&c67x00->hpi.lock); 142 c67x00->hpi.regstep = pdata->hpi_regstep; 143 c67x00->pdata = dev_get_platdata(&pdev->dev); 144 c67x00->pdev = pdev; 145 146 c67x00_ll_init(c67x00); 147 c67x00_ll_hpi_reg_init(c67x00); 148 149 ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00); 150 if (ret) { 151 dev_err(&pdev->dev, "Cannot claim IRQ\n"); 152 goto request_irq_failed; 153 } 154 155 ret = c67x00_ll_reset(c67x00); 156 if (ret) { 157 dev_err(&pdev->dev, "Device reset failed\n"); 158 goto reset_failed; 159 } 160 161 for (i = 0; i < C67X00_SIES; i++) 162 c67x00_probe_sie(&c67x00->sie[i], c67x00, i); 163 164 platform_set_drvdata(pdev, c67x00); 165 166 return 0; 167 168 reset_failed: 169 free_irq(res2->start, c67x00); 170 request_irq_failed: 171 iounmap(c67x00->hpi.base); 172 map_failed: 173 release_mem_region(res->start, resource_size(res)); 174 request_mem_failed: 175 kfree(c67x00); 176 177 return ret; 178} 179 180static int c67x00_drv_remove(struct platform_device *pdev) 181{ 182 struct c67x00_device *c67x00 = platform_get_drvdata(pdev); 183 struct resource *res; 184 int i; 185 186 for (i = 0; i < C67X00_SIES; i++) 187 c67x00_remove_sie(&c67x00->sie[i]); 188 189 c67x00_ll_release(c67x00); 190 191 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 192 free_irq(res->start, c67x00); 193 194 iounmap(c67x00->hpi.base); 195 196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197 release_mem_region(res->start, resource_size(res)); 198 199 kfree(c67x00); 200 201 return 0; 202} 203 204static struct platform_driver c67x00_driver = { 205 .probe = c67x00_drv_probe, 206 .remove = c67x00_drv_remove, 207 .driver = { 208 .name = "c67x00", 209 }, 210}; 211 212module_platform_driver(c67x00_driver); 213 214MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely"); 215MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver"); 216MODULE_LICENSE("GPL"); 217MODULE_ALIAS("platform:c67x00");