xref: /OK3568_Linux_fs/kernel/drivers/usb/isp1760/isp1760-core.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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