isp1760-if.c 11.1 KB
Newer Older
S
Sebastian Siewior 已提交
1 2 3 4 5
/*
 * Glue code for the ISP1760 driver and bus
 * Currently there is support for
 * - OpenFirmware
 * - PCI
6
 * - PDEV (generic platform device centralized driver model)
S
Sebastian Siewior 已提交
7 8 9 10 11 12 13
 *
 * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
 *
 */

#include <linux/usb.h>
#include <linux/io.h>
14
#include <linux/module.h>
15
#include <linux/platform_device.h>
16
#include <linux/usb/isp1760.h>
17
#include <linux/usb/hcd.h>
S
Sebastian Siewior 已提交
18 19 20

#include "isp1760-hcd.h"

D
David Miller 已提交
21
#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
22
#include <linux/slab.h>
S
Sebastian Siewior 已提交
23 24
#include <linux/of.h>
#include <linux/of_platform.h>
25 26
#include <linux/of_address.h>
#include <linux/of_irq.h>
27
#include <linux/of_gpio.h>
S
Sebastian Siewior 已提交
28 29
#endif

30
#ifdef CONFIG_PCI
S
Sebastian Siewior 已提交
31 32 33
#include <linux/pci.h>
#endif

D
David Miller 已提交
34
#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
35 36 37 38 39
struct isp1760 {
	struct usb_hcd *hcd;
	int rst_gpio;
};

40
static int of_isp1760_probe(struct platform_device *dev)
S
Sebastian Siewior 已提交
41
{
42
	struct isp1760 *drvdata;
43
	struct device_node *dp = dev->dev.of_node;
S
Sebastian Siewior 已提交
44 45 46 47
	struct resource *res;
	struct resource memory;
	struct of_irq oirq;
	int virq;
T
Tobias Klauser 已提交
48
	resource_size_t res_len;
S
Sebastian Siewior 已提交
49
	int ret;
50
	unsigned int devflags = 0;
51
	enum of_gpio_flags gpio_flags;
52
	u32 bus_width = 0;
53 54 55 56

	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata)
		return -ENOMEM;
S
Sebastian Siewior 已提交
57 58

	ret = of_address_to_resource(dp, 0, &memory);
59 60 61 62
	if (ret) {
		ret = -ENXIO;
		goto free_data;
	}
S
Sebastian Siewior 已提交
63

T
Tobias Klauser 已提交
64 65 66
	res_len = resource_size(&memory);

	res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
67 68 69 70
	if (!res) {
		ret = -EBUSY;
		goto free_data;
	}
S
Sebastian Siewior 已提交
71 72 73 74 75 76 77 78 79

	if (of_irq_map_one(dp, 0, &oirq)) {
		ret = -ENODEV;
		goto release_reg;
	}

	virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
			oirq.size);

80 81 82 83
	if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
		devflags |= ISP1760_FLAG_ISP1761;

	/* Some systems wire up only 16 of the 32 data lines */
84 85
	of_property_read_u32(dp, "bus-width", &bus_width);
	if (bus_width == 16)
86 87 88 89 90 91 92 93 94 95 96 97 98 99
		devflags |= ISP1760_FLAG_BUS_WIDTH_16;

	if (of_get_property(dp, "port1-otg", NULL) != NULL)
		devflags |= ISP1760_FLAG_OTG_EN;

	if (of_get_property(dp, "analog-oc", NULL) != NULL)
		devflags |= ISP1760_FLAG_ANALOG_OC;

	if (of_get_property(dp, "dack-polarity", NULL) != NULL)
		devflags |= ISP1760_FLAG_DACK_POL_HIGH;

	if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
		devflags |= ISP1760_FLAG_DREQ_POL_HIGH;

100 101 102 103 104 105 106 107 108 109 110 111 112
	drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
	if (gpio_is_valid(drvdata->rst_gpio)) {
		ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
		if (!ret) {
			if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
				devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
				gpio_direction_output(drvdata->rst_gpio, 0);
			} else {
				gpio_direction_output(drvdata->rst_gpio, 1);
			}
		} else {
			drvdata->rst_gpio = ret;
		}
S
Sebastian Siewior 已提交
113 114
	}

115 116 117 118 119 120 121 122 123 124
	drvdata->hcd = isp1760_register(memory.start, res_len, virq,
					IRQF_SHARED, drvdata->rst_gpio,
					&dev->dev, dev_name(&dev->dev),
					devflags);
	if (IS_ERR(drvdata->hcd)) {
		ret = PTR_ERR(drvdata->hcd);
		goto free_gpio;
	}

	dev_set_drvdata(&dev->dev, drvdata);
S
Sebastian Siewior 已提交
125 126
	return ret;

127 128 129
free_gpio:
	if (gpio_is_valid(drvdata->rst_gpio))
		gpio_free(drvdata->rst_gpio);
S
Sebastian Siewior 已提交
130
release_reg:
T
Tobias Klauser 已提交
131
	release_mem_region(memory.start, res_len);
132
free_data:
133
	kfree(drvdata);
S
Sebastian Siewior 已提交
134 135 136
	return ret;
}

137
static int of_isp1760_remove(struct platform_device *dev)
S
Sebastian Siewior 已提交
138
{
139
	struct isp1760 *drvdata = dev_get_drvdata(&dev->dev);
S
Sebastian Siewior 已提交
140 141 142

	dev_set_drvdata(&dev->dev, NULL);

143 144 145 146 147 148 149 150 151
	usb_remove_hcd(drvdata->hcd);
	iounmap(drvdata->hcd->regs);
	release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
	usb_put_hcd(drvdata->hcd);

	if (gpio_is_valid(drvdata->rst_gpio))
		gpio_free(drvdata->rst_gpio);

	kfree(drvdata);
S
Sebastian Siewior 已提交
152 153 154
	return 0;
}

155
static const struct of_device_id of_isp1760_match[] = {
S
Sebastian Siewior 已提交
156 157 158
	{
		.compatible = "nxp,usb-isp1760",
	},
159 160 161
	{
		.compatible = "nxp,usb-isp1761",
	},
S
Sebastian Siewior 已提交
162 163 164 165
	{ },
};
MODULE_DEVICE_TABLE(of, of_isp1760_match);

166
static struct platform_driver isp1760_of_driver = {
167 168 169 170 171
	.driver = {
		.name = "nxp-isp1760",
		.owner = THIS_MODULE,
		.of_match_table = of_isp1760_match,
	},
S
Sebastian Siewior 已提交
172 173 174 175 176
	.probe          = of_isp1760_probe,
	.remove         = of_isp1760_remove,
};
#endif

177
#ifdef CONFIG_PCI
S
Sebastian Siewior 已提交
178 179 180 181 182 183 184
static int __devinit isp1761_pci_probe(struct pci_dev *dev,
		const struct pci_device_id *id)
{
	u8 latency, limit;
	__u32 reg_data;
	int retry_count;
	struct usb_hcd *hcd;
185
	unsigned int devflags = 0;
186 187 188 189 190 191 192 193 194
	int ret_status = 0;

	resource_size_t pci_mem_phy0;
	resource_size_t memlength;

	u8 __iomem *chip_addr;
	u8 __iomem *iobase;
	resource_size_t nxp_pci_io_base;
	resource_size_t iolength;
S
Sebastian Siewior 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

	if (usb_disabled())
		return -ENODEV;

	if (pci_enable_device(dev) < 0)
		return -ENODEV;

	if (!dev->irq)
		return -ENODEV;

	/* Grab the PLX PCI mem maped port start address we need  */
	nxp_pci_io_base = pci_resource_start(dev, 0);
	iolength = pci_resource_len(dev, 0);

	if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
		printk(KERN_ERR "request region #1\n");
		return -EBUSY;
	}

	iobase = ioremap_nocache(nxp_pci_io_base, iolength);
	if (!iobase) {
		printk(KERN_ERR "ioremap #1\n");
217 218
		ret_status = -ENOMEM;
		goto cleanup1;
S
Sebastian Siewior 已提交
219 220 221
	}
	/* Grab the PLX PCI shared memory of the ISP 1761 we need  */
	pci_mem_phy0 = pci_resource_start(dev, 3);
222 223 224 225 226
	memlength = pci_resource_len(dev, 3);
	if (memlength < 0xffff) {
		printk(KERN_ERR "memory length for this resource is wrong\n");
		ret_status = -ENOMEM;
		goto cleanup2;
S
Sebastian Siewior 已提交
227 228
	}

229
	if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
S
Sebastian Siewior 已提交
230
		printk(KERN_ERR "host controller already in use\n");
231 232 233 234 235 236 237 238 239 240
		ret_status = -EBUSY;
		goto cleanup2;
	}

	/* map available memory */
	chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
	if (!chip_addr) {
		printk(KERN_ERR "Error ioremap failed\n");
		ret_status = -ENOMEM;
		goto cleanup3;
S
Sebastian Siewior 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
	}

	/* bad pci latencies can contribute to overruns */
	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
	if (latency) {
		pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
		if (limit && limit < latency)
			pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
	}

	/* Try to check whether we can access Scratch Register of
	 * Host Controller or not. The initial PCI access is retried until
	 * local init for the PCI bridge is completed
	 */
	retry_count = 20;
	reg_data = 0;
	while ((reg_data != 0xFACE) && retry_count) {
		/*by default host is in 16bit mode, so
		 * io operations at this stage must be 16 bit
		 * */
		writel(0xface, chip_addr + HC_SCRATCH_REG);
		udelay(100);
263
		reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
S
Sebastian Siewior 已提交
264 265 266
		retry_count--;
	}

267 268
	iounmap(chip_addr);

S
Sebastian Siewior 已提交
269 270 271 272
	/* Host Controller presence is detected by writing to scratch register
	 * and reading back and checking the contents are same or not
	 */
	if (reg_data != 0xFACE) {
273
		dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
274 275
		ret_status = -ENOMEM;
		goto cleanup3;
S
Sebastian Siewior 已提交
276 277 278 279
	}

	pci_set_master(dev);

280 281 282 283 284
	/* configure PLX PCI chip to pass interrupts */
#define PLX_INT_CSR_REG 0x68
	reg_data = readl(iobase + PLX_INT_CSR_REG);
	reg_data |= 0x900;
	writel(reg_data, iobase + PLX_INT_CSR_REG);
S
Sebastian Siewior 已提交
285 286

	dev->dev.dma_mask = NULL;
287
	hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
288
		IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
289
		devflags);
290 291 292
	if (IS_ERR(hcd)) {
		ret_status = -ENODEV;
		goto cleanup3;
293
	}
294 295

	/* done with PLX IO access */
S
Sebastian Siewior 已提交
296 297
	iounmap(iobase);
	release_mem_region(nxp_pci_io_base, iolength);
298 299 300 301 302 303 304 305 306 307 308

	pci_set_drvdata(dev, hcd);
	return 0;

cleanup3:
	release_mem_region(pci_mem_phy0, memlength);
cleanup2:
	iounmap(iobase);
cleanup1:
	release_mem_region(nxp_pci_io_base, iolength);
	return ret_status;
S
Sebastian Siewior 已提交
309
}
310

S
Sebastian Siewior 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
static void isp1761_pci_remove(struct pci_dev *dev)
{
	struct usb_hcd *hcd;

	hcd = pci_get_drvdata(dev);

	usb_remove_hcd(hcd);
	iounmap(hcd->regs);
	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
	usb_put_hcd(hcd);

	pci_disable_device(dev);
}

static void isp1761_pci_shutdown(struct pci_dev *dev)
{
	printk(KERN_ERR "ips1761_pci_shutdown\n");
}

330 331 332 333 334 335 336 337 338 339
static const struct pci_device_id isp1760_plx [] = {
	{
		.class          = PCI_CLASS_BRIDGE_OTHER << 8,
		.class_mask     = ~0,
		.vendor		= PCI_VENDOR_ID_PLX,
		.device		= 0x5406,
		.subvendor	= PCI_VENDOR_ID_PLX,
		.subdevice	= 0x9054,
	},
	{ }
S
Sebastian Siewior 已提交
340 341 342 343 344 345 346 347 348 349 350 351
};
MODULE_DEVICE_TABLE(pci, isp1760_plx);

static struct pci_driver isp1761_pci_driver = {
	.name =         "isp1760",
	.id_table =     isp1760_plx,
	.probe =        isp1761_pci_probe,
	.remove =       isp1761_pci_remove,
	.shutdown =     isp1761_pci_shutdown,
};
#endif

352 353 354 355 356 357 358
static int __devinit isp1760_plat_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct usb_hcd *hcd;
	struct resource *mem_res;
	struct resource *irq_res;
	resource_size_t mem_size;
359 360
	struct isp1760_platform_data *priv = pdev->dev.platform_data;
	unsigned int devflags = 0;
Y
Yong Zhang 已提交
361
	unsigned long irqflags = IRQF_SHARED;
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem_res) {
		pr_warning("isp1760: Memory resource not available\n");
		ret = -ENODEV;
		goto out;
	}
	mem_size = resource_size(mem_res);
	if (!request_mem_region(mem_res->start, mem_size, "isp1760")) {
		pr_warning("isp1760: Cannot reserve the memory resource\n");
		ret = -EBUSY;
		goto out;
	}

	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!irq_res) {
		pr_warning("isp1760: IRQ resource not available\n");
		return -ENODEV;
	}
	irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
	if (priv) {
		if (priv->is_isp1761)
			devflags |= ISP1760_FLAG_ISP1761;
		if (priv->bus_width_16)
			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
		if (priv->port1_otg)
			devflags |= ISP1760_FLAG_OTG_EN;
		if (priv->analog_oc)
			devflags |= ISP1760_FLAG_ANALOG_OC;
		if (priv->dack_polarity_high)
			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
		if (priv->dreq_polarity_high)
			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
	}

398
	hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
399 400
			       irqflags, -ENOENT,
			       &pdev->dev, dev_name(&pdev->dev), devflags);
401 402 403

	dev_set_drvdata(&pdev->dev, hcd);

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
	if (IS_ERR(hcd)) {
		pr_warning("isp1760: Failed to register the HCD device\n");
		ret = -ENODEV;
		goto cleanup;
	}

	pr_info("ISP1760 USB device initialised\n");
	return ret;

cleanup:
	release_mem_region(mem_res->start, mem_size);
out:
	return ret;
}

static int __devexit isp1760_plat_remove(struct platform_device *pdev)
{
	struct resource *mem_res;
	resource_size_t mem_size;
423 424 425
	struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);

	usb_remove_hcd(hcd);
426 427 428 429 430

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	mem_size = resource_size(mem_res);
	release_mem_region(mem_res->start, mem_size);

431 432
	usb_put_hcd(hcd);

433 434 435 436 437
	return 0;
}

static struct platform_driver isp1760_plat_driver = {
	.probe	= isp1760_plat_probe,
438
	.remove	= __devexit_p(isp1760_plat_remove),
439 440 441 442 443
	.driver	= {
		.name	= "isp1760",
	},
};

S
Sebastian Siewior 已提交
444 445
static int __init isp1760_init(void)
{
446
	int ret, any_ret = -ENODEV;
S
Sebastian Siewior 已提交
447 448 449

	init_kmem_once();

450 451 452
	ret = platform_driver_register(&isp1760_plat_driver);
	if (!ret)
		any_ret = 0;
D
David Miller 已提交
453
#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
454
	ret = platform_driver_register(&isp1760_of_driver);
455 456
	if (!ret)
		any_ret = 0;
S
Sebastian Siewior 已提交
457
#endif
458
#ifdef CONFIG_PCI
S
Sebastian Siewior 已提交
459
	ret = pci_register_driver(&isp1761_pci_driver);
460 461
	if (!ret)
		any_ret = 0;
S
Sebastian Siewior 已提交
462 463
#endif

464 465 466
	if (any_ret)
		deinit_kmem_cache();
	return any_ret;
S
Sebastian Siewior 已提交
467 468 469 470 471
}
module_init(isp1760_init);

static void __exit isp1760_exit(void)
{
472
	platform_driver_unregister(&isp1760_plat_driver);
D
David Miller 已提交
473
#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
474
	platform_driver_unregister(&isp1760_of_driver);
S
Sebastian Siewior 已提交
475
#endif
476
#ifdef CONFIG_PCI
S
Sebastian Siewior 已提交
477 478 479 480 481
	pci_unregister_driver(&isp1761_pci_driver);
#endif
	deinit_kmem_cache();
}
module_exit(isp1760_exit);