18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the NXP ISP1760 chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Laurent Pinchart 68c2ecf20Sopenharmony_ci * Copyright 2007 Sebastian Siewior 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contacts: 98c2ecf20Sopenharmony_ci * Sebastian Siewior <bigeasy@linutronix.de> 108c2ecf20Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/usb.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "isp1760-core.h" 228c2ecf20Sopenharmony_ci#include "isp1760-hcd.h" 238c2ecf20Sopenharmony_ci#include "isp1760-regs.h" 248c2ecf20Sopenharmony_ci#include "isp1760-udc.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void isp1760_init_core(struct isp1760_device *isp) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u32 otgctrl; 298c2ecf20Sopenharmony_ci u32 hwmode; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* Low-level chip reset */ 328c2ecf20Sopenharmony_ci if (isp->rst_gpio) { 338c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(isp->rst_gpio, 1); 348c2ecf20Sopenharmony_ci msleep(50); 358c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(isp->rst_gpio, 0); 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * Reset the host controller, including the CPU interface 408c2ecf20Sopenharmony_ci * configuration. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL); 438c2ecf20Sopenharmony_ci msleep(100); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Setup HW Mode Control: This assumes a level active-low interrupt */ 468c2ecf20Sopenharmony_ci hwmode = HW_DATA_BUS_32BIT; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16) 498c2ecf20Sopenharmony_ci hwmode &= ~HW_DATA_BUS_32BIT; 508c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_ANALOG_OC) 518c2ecf20Sopenharmony_ci hwmode |= HW_ANA_DIGI_OC; 528c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH) 538c2ecf20Sopenharmony_ci hwmode |= HW_DACK_POL_HIGH; 548c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH) 558c2ecf20Sopenharmony_ci hwmode |= HW_DREQ_POL_HIGH; 568c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH) 578c2ecf20Sopenharmony_ci hwmode |= HW_INTR_HIGH_ACT; 588c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) 598c2ecf20Sopenharmony_ci hwmode |= HW_INTR_EDGE_TRIG; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* 628c2ecf20Sopenharmony_ci * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC 638c2ecf20Sopenharmony_ci * IRQ line for both the host and device controllers. Hardcode IRQ 648c2ecf20Sopenharmony_ci * sharing for now and disable the DC interrupts globally to avoid 658c2ecf20Sopenharmony_ci * spurious interrupts during HCD registration. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci if (isp->devflags & ISP1760_FLAG_ISP1761) { 688c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, DC_MODE, 0); 698c2ecf20Sopenharmony_ci hwmode |= HW_COMN_IRQ; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * We have to set this first in case we're in 16-bit mode. 748c2ecf20Sopenharmony_ci * Write it twice to ensure correct upper bits if switching 758c2ecf20Sopenharmony_ci * to 16-bit mode. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); 788c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * PORT 1 Control register of the ISP1760 is the OTG control register 828c2ecf20Sopenharmony_ci * on ISP1761. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * TODO: Really support OTG. For now we configure port 1 in device mode 858c2ecf20Sopenharmony_ci * when OTG is requested. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if ((isp->devflags & ISP1760_FLAG_ISP1761) && 888c2ecf20Sopenharmony_ci (isp->devflags & ISP1760_FLAG_OTG_EN)) 898c2ecf20Sopenharmony_ci otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16) 908c2ecf20Sopenharmony_ci | HW_OTG_DISABLE; 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci otgctrl = (HW_SW_SEL_HC_DC << 16) 938c2ecf20Sopenharmony_ci | (HW_VBUS_DRV | HW_SEL_CP_EXT); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci dev_info(isp->dev, "bus width: %u, oc: %s\n", 988c2ecf20Sopenharmony_ci isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, 998c2ecf20Sopenharmony_ci isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid isp1760_set_pullup(struct isp1760_device *isp, bool enable) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci isp1760_write32(isp->regs, HW_OTG_CTRL_SET, 1058c2ecf20Sopenharmony_ci enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ciint isp1760_register(struct resource *mem, int irq, unsigned long irqflags, 1098c2ecf20Sopenharmony_ci struct device *dev, unsigned int devflags) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct isp1760_device *isp; 1128c2ecf20Sopenharmony_ci bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761); 1138c2ecf20Sopenharmony_ci int ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * If neither the HCD not the UDC is enabled return an error, as no 1178c2ecf20Sopenharmony_ci * device would be registered. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) && 1208c2ecf20Sopenharmony_ci (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled)) 1218c2ecf20Sopenharmony_ci return -ENODEV; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); 1248c2ecf20Sopenharmony_ci if (!isp) 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci isp->dev = dev; 1288c2ecf20Sopenharmony_ci isp->devflags = devflags; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); 1318c2ecf20Sopenharmony_ci if (IS_ERR(isp->rst_gpio)) 1328c2ecf20Sopenharmony_ci return PTR_ERR(isp->rst_gpio); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci isp->regs = devm_ioremap_resource(dev, mem); 1358c2ecf20Sopenharmony_ci if (IS_ERR(isp->regs)) 1368c2ecf20Sopenharmony_ci return PTR_ERR(isp->regs); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci isp1760_init_core(isp); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) { 1418c2ecf20Sopenharmony_ci ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, 1428c2ecf20Sopenharmony_ci irqflags | IRQF_SHARED, dev); 1438c2ecf20Sopenharmony_ci if (ret < 0) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) { 1488c2ecf20Sopenharmony_ci ret = isp1760_udc_register(isp, irq, irqflags); 1498c2ecf20Sopenharmony_ci if (ret < 0) { 1508c2ecf20Sopenharmony_ci isp1760_hcd_unregister(&isp->hcd); 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci dev_set_drvdata(dev, isp); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid isp1760_unregister(struct device *dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct isp1760_device *isp = dev_get_drvdata(dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci isp1760_udc_unregister(isp); 1658c2ecf20Sopenharmony_ci isp1760_hcd_unregister(&isp->hcd); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP"); 1698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>"); 1708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 171