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