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