1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for the NXP ISP1760 chip
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2014 Laurent Pinchart
6*4882a593Smuzhiyun * Copyright 2007 Sebastian Siewior
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Contacts:
9*4882a593Smuzhiyun * Sebastian Siewior <bigeasy@linutronix.de>
10*4882a593Smuzhiyun * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/delay.h>
14*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/usb.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include "isp1760-core.h"
22*4882a593Smuzhiyun #include "isp1760-hcd.h"
23*4882a593Smuzhiyun #include "isp1760-regs.h"
24*4882a593Smuzhiyun #include "isp1760-udc.h"
25*4882a593Smuzhiyun
isp1760_init_core(struct isp1760_device * isp)26*4882a593Smuzhiyun static void isp1760_init_core(struct isp1760_device *isp)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun u32 otgctrl;
29*4882a593Smuzhiyun u32 hwmode;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* Low-level chip reset */
32*4882a593Smuzhiyun if (isp->rst_gpio) {
33*4882a593Smuzhiyun gpiod_set_value_cansleep(isp->rst_gpio, 1);
34*4882a593Smuzhiyun msleep(50);
35*4882a593Smuzhiyun gpiod_set_value_cansleep(isp->rst_gpio, 0);
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /*
39*4882a593Smuzhiyun * Reset the host controller, including the CPU interface
40*4882a593Smuzhiyun * configuration.
41*4882a593Smuzhiyun */
42*4882a593Smuzhiyun isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
43*4882a593Smuzhiyun msleep(100);
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* Setup HW Mode Control: This assumes a level active-low interrupt */
46*4882a593Smuzhiyun hwmode = HW_DATA_BUS_32BIT;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
49*4882a593Smuzhiyun hwmode &= ~HW_DATA_BUS_32BIT;
50*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
51*4882a593Smuzhiyun hwmode |= HW_ANA_DIGI_OC;
52*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
53*4882a593Smuzhiyun hwmode |= HW_DACK_POL_HIGH;
54*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
55*4882a593Smuzhiyun hwmode |= HW_DREQ_POL_HIGH;
56*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
57*4882a593Smuzhiyun hwmode |= HW_INTR_HIGH_ACT;
58*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
59*4882a593Smuzhiyun hwmode |= HW_INTR_EDGE_TRIG;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun /*
62*4882a593Smuzhiyun * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
63*4882a593Smuzhiyun * IRQ line for both the host and device controllers. Hardcode IRQ
64*4882a593Smuzhiyun * sharing for now and disable the DC interrupts globally to avoid
65*4882a593Smuzhiyun * spurious interrupts during HCD registration.
66*4882a593Smuzhiyun */
67*4882a593Smuzhiyun if (isp->devflags & ISP1760_FLAG_ISP1761) {
68*4882a593Smuzhiyun isp1760_write32(isp->regs, DC_MODE, 0);
69*4882a593Smuzhiyun hwmode |= HW_COMN_IRQ;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /*
73*4882a593Smuzhiyun * We have to set this first in case we're in 16-bit mode.
74*4882a593Smuzhiyun * Write it twice to ensure correct upper bits if switching
75*4882a593Smuzhiyun * to 16-bit mode.
76*4882a593Smuzhiyun */
77*4882a593Smuzhiyun isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
78*4882a593Smuzhiyun isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /*
81*4882a593Smuzhiyun * PORT 1 Control register of the ISP1760 is the OTG control register
82*4882a593Smuzhiyun * on ISP1761.
83*4882a593Smuzhiyun *
84*4882a593Smuzhiyun * TODO: Really support OTG. For now we configure port 1 in device mode
85*4882a593Smuzhiyun * when OTG is requested.
86*4882a593Smuzhiyun */
87*4882a593Smuzhiyun if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
88*4882a593Smuzhiyun (isp->devflags & ISP1760_FLAG_OTG_EN))
89*4882a593Smuzhiyun otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
90*4882a593Smuzhiyun | HW_OTG_DISABLE;
91*4882a593Smuzhiyun else
92*4882a593Smuzhiyun otgctrl = (HW_SW_SEL_HC_DC << 16)
93*4882a593Smuzhiyun | (HW_VBUS_DRV | HW_SEL_CP_EXT);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun dev_info(isp->dev, "bus width: %u, oc: %s\n",
98*4882a593Smuzhiyun isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
99*4882a593Smuzhiyun isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
isp1760_set_pullup(struct isp1760_device * isp,bool enable)102*4882a593Smuzhiyun void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
105*4882a593Smuzhiyun enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
isp1760_register(struct resource * mem,int irq,unsigned long irqflags,struct device * dev,unsigned int devflags)108*4882a593Smuzhiyun int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
109*4882a593Smuzhiyun struct device *dev, unsigned int devflags)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct isp1760_device *isp;
112*4882a593Smuzhiyun bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
113*4882a593Smuzhiyun int ret;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun /*
116*4882a593Smuzhiyun * If neither the HCD not the UDC is enabled return an error, as no
117*4882a593Smuzhiyun * device would be registered.
118*4882a593Smuzhiyun */
119*4882a593Smuzhiyun if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
120*4882a593Smuzhiyun (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
121*4882a593Smuzhiyun return -ENODEV;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
124*4882a593Smuzhiyun if (!isp)
125*4882a593Smuzhiyun return -ENOMEM;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun isp->dev = dev;
128*4882a593Smuzhiyun isp->devflags = devflags;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
131*4882a593Smuzhiyun if (IS_ERR(isp->rst_gpio))
132*4882a593Smuzhiyun return PTR_ERR(isp->rst_gpio);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun isp->regs = devm_ioremap_resource(dev, mem);
135*4882a593Smuzhiyun if (IS_ERR(isp->regs))
136*4882a593Smuzhiyun return PTR_ERR(isp->regs);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun isp1760_init_core(isp);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) {
141*4882a593Smuzhiyun ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq,
142*4882a593Smuzhiyun irqflags | IRQF_SHARED, dev);
143*4882a593Smuzhiyun if (ret < 0)
144*4882a593Smuzhiyun return ret;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
148*4882a593Smuzhiyun ret = isp1760_udc_register(isp, irq, irqflags);
149*4882a593Smuzhiyun if (ret < 0) {
150*4882a593Smuzhiyun isp1760_hcd_unregister(&isp->hcd);
151*4882a593Smuzhiyun return ret;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun dev_set_drvdata(dev, isp);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
isp1760_unregister(struct device * dev)160*4882a593Smuzhiyun void isp1760_unregister(struct device *dev)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun struct isp1760_device *isp = dev_get_drvdata(dev);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun isp1760_udc_unregister(isp);
165*4882a593Smuzhiyun isp1760_hcd_unregister(&isp->hcd);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
169*4882a593Smuzhiyun MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
170*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
171