162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * c67x00-drv.c: Cypress C67X00 USB Common infrastructure 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Barco N.V. 662306a36Sopenharmony_ci * Derived from the Cypress cy7c67200/300 ezusb linux driver and 762306a36Sopenharmony_ci * based on multiple host controller drivers inside the linux kernel. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * This file implements the common infrastructure for using the c67x00. 1262306a36Sopenharmony_ci * It is both the link between the platform configuration and subdrivers and 1362306a36Sopenharmony_ci * the link between the common hardware parts and the subdrivers (e.g. 1462306a36Sopenharmony_ci * interrupt handling). 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * The c67x00 has 2 SIE's (serial interface engine) which can be configured 1762306a36Sopenharmony_ci * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG). 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Depending on the platform configuration, the SIE's are created and 2062306a36Sopenharmony_ci * the corresponding subdriver is initialized (c67x00_probe_sie). 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/device.h> 2462306a36Sopenharmony_ci#include <linux/io.h> 2562306a36Sopenharmony_ci#include <linux/list.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/usb.h> 2962306a36Sopenharmony_ci#include <linux/usb/c67x00.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "c67x00.h" 3262306a36Sopenharmony_ci#include "c67x00-hcd.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void c67x00_probe_sie(struct c67x00_sie *sie, 3562306a36Sopenharmony_ci struct c67x00_device *dev, int sie_num) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci spin_lock_init(&sie->lock); 3862306a36Sopenharmony_ci sie->dev = dev; 3962306a36Sopenharmony_ci sie->sie_num = sie_num; 4062306a36Sopenharmony_ci sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci switch (sie->mode) { 4362306a36Sopenharmony_ci case C67X00_SIE_HOST: 4462306a36Sopenharmony_ci c67x00_hcd_probe(sie); 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci case C67X00_SIE_UNUSED: 4862306a36Sopenharmony_ci dev_info(sie_dev(sie), 4962306a36Sopenharmony_ci "Not using SIE %d as requested\n", sie->sie_num); 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci default: 5362306a36Sopenharmony_ci dev_err(sie_dev(sie), 5462306a36Sopenharmony_ci "Unsupported configuration: 0x%x for SIE %d\n", 5562306a36Sopenharmony_ci sie->mode, sie->sie_num); 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void c67x00_remove_sie(struct c67x00_sie *sie) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci switch (sie->mode) { 6362306a36Sopenharmony_ci case C67X00_SIE_HOST: 6462306a36Sopenharmony_ci c67x00_hcd_remove(sie); 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci default: 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic irqreturn_t c67x00_irq(int irq, void *__dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct c67x00_device *c67x00 = __dev; 7562306a36Sopenharmony_ci struct c67x00_sie *sie; 7662306a36Sopenharmony_ci u16 msg, int_status; 7762306a36Sopenharmony_ci int i, count = 8; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci int_status = c67x00_ll_hpi_status(c67x00); 8062306a36Sopenharmony_ci if (!int_status) 8162306a36Sopenharmony_ci return IRQ_NONE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci while (int_status != 0 && (count-- >= 0)) { 8462306a36Sopenharmony_ci c67x00_ll_irq(c67x00, int_status); 8562306a36Sopenharmony_ci for (i = 0; i < C67X00_SIES; i++) { 8662306a36Sopenharmony_ci sie = &c67x00->sie[i]; 8762306a36Sopenharmony_ci msg = 0; 8862306a36Sopenharmony_ci if (int_status & SIEMSG_FLG(i)) 8962306a36Sopenharmony_ci msg = c67x00_ll_fetch_siemsg(c67x00, i); 9062306a36Sopenharmony_ci if (sie->irq) 9162306a36Sopenharmony_ci sie->irq(sie, int_status, msg); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci int_status = c67x00_ll_hpi_status(c67x00); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (int_status) 9762306a36Sopenharmony_ci dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! " 9862306a36Sopenharmony_ci "status = 0x%04x\n", int_status); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return IRQ_HANDLED; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int c67x00_drv_probe(struct platform_device *pdev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct c67x00_device *c67x00; 10862306a36Sopenharmony_ci struct c67x00_platform_data *pdata; 10962306a36Sopenharmony_ci struct resource *res, *res2; 11062306a36Sopenharmony_ci int ret, i; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11362306a36Sopenharmony_ci if (!res) 11462306a36Sopenharmony_ci return -ENODEV; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 11762306a36Sopenharmony_ci if (!res2) 11862306a36Sopenharmony_ci return -ENODEV; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 12162306a36Sopenharmony_ci if (!pdata) 12262306a36Sopenharmony_ci return -ENODEV; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL); 12562306a36Sopenharmony_ci if (!c67x00) 12662306a36Sopenharmony_ci return -ENOMEM; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!request_mem_region(res->start, resource_size(res), 12962306a36Sopenharmony_ci pdev->name)) { 13062306a36Sopenharmony_ci dev_err(&pdev->dev, "Memory region busy\n"); 13162306a36Sopenharmony_ci ret = -EBUSY; 13262306a36Sopenharmony_ci goto request_mem_failed; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci c67x00->hpi.base = ioremap(res->start, resource_size(res)); 13562306a36Sopenharmony_ci if (!c67x00->hpi.base) { 13662306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to map HPI registers\n"); 13762306a36Sopenharmony_ci ret = -EIO; 13862306a36Sopenharmony_ci goto map_failed; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci spin_lock_init(&c67x00->hpi.lock); 14262306a36Sopenharmony_ci c67x00->hpi.regstep = pdata->hpi_regstep; 14362306a36Sopenharmony_ci c67x00->pdata = dev_get_platdata(&pdev->dev); 14462306a36Sopenharmony_ci c67x00->pdev = pdev; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci c67x00_ll_init(c67x00); 14762306a36Sopenharmony_ci c67x00_ll_hpi_reg_init(c67x00); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00); 15062306a36Sopenharmony_ci if (ret) { 15162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot claim IRQ\n"); 15262306a36Sopenharmony_ci goto request_irq_failed; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = c67x00_ll_reset(c67x00); 15662306a36Sopenharmony_ci if (ret) { 15762306a36Sopenharmony_ci dev_err(&pdev->dev, "Device reset failed\n"); 15862306a36Sopenharmony_ci goto reset_failed; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < C67X00_SIES; i++) 16262306a36Sopenharmony_ci c67x00_probe_sie(&c67x00->sie[i], c67x00, i); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci platform_set_drvdata(pdev, c67x00); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci reset_failed: 16962306a36Sopenharmony_ci free_irq(res2->start, c67x00); 17062306a36Sopenharmony_ci request_irq_failed: 17162306a36Sopenharmony_ci iounmap(c67x00->hpi.base); 17262306a36Sopenharmony_ci map_failed: 17362306a36Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 17462306a36Sopenharmony_ci request_mem_failed: 17562306a36Sopenharmony_ci kfree(c67x00); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void c67x00_drv_remove(struct platform_device *pdev) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct c67x00_device *c67x00 = platform_get_drvdata(pdev); 18362306a36Sopenharmony_ci struct resource *res; 18462306a36Sopenharmony_ci int i; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < C67X00_SIES; i++) 18762306a36Sopenharmony_ci c67x00_remove_sie(&c67x00->sie[i]); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci c67x00_ll_release(c67x00); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 19262306a36Sopenharmony_ci free_irq(res->start, c67x00); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci iounmap(c67x00->hpi.base); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 19762306a36Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci kfree(c67x00); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic struct platform_driver c67x00_driver = { 20362306a36Sopenharmony_ci .probe = c67x00_drv_probe, 20462306a36Sopenharmony_ci .remove_new = c67x00_drv_remove, 20562306a36Sopenharmony_ci .driver = { 20662306a36Sopenharmony_ci .name = "c67x00", 20762306a36Sopenharmony_ci }, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cimodule_platform_driver(c67x00_driver); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciMODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely"); 21362306a36Sopenharmony_ciMODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver"); 21462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 21562306a36Sopenharmony_ciMODULE_ALIAS("platform:c67x00"); 216