aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c2410/usb-simtec.c
blob: 199bb4ffe689d442524a49d20051c3afd6edb83b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* linux/arch/arm/mach-s3c2410/usb-simtec.c
 *
 * Copyright 2004-2005 Simtec Electronics
 *   Ben Dooks <ben@simtec.co.uk>
 *
 * http://www.simtec.co.uk/products/EB2410ITX/
 *
 * Simtec BAST and Thorcom VR1000 USB port support functions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#define DEBUG

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/io.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>

#include <mach/bast-map.h>
#include <mach/bast-irq.h>

#include <mach/hardware.h>
#include <asm/irq.h>

#include <plat/usb-control.h>
#include <plat/devs.h>

#include "usb-simtec.h"

/* control power and monitor over-current events on various Simtec
 * designed boards.
*/

static unsigned int power_state[2];

static void
usb_simtec_powercontrol(int port, int to)
{
	pr_debug("usb_simtec_powercontrol(%d,%d)\n", port, to);

	power_state[port] = to;

	if (power_state[0] && power_state[1])
		gpio_set_value(S3C2410_GPB(4), 0);
	else
		gpio_set_value(S3C2410_GPB(4), 1);
}

static irqreturn_t
usb_simtec_ocirq(int irq, void *pw)
{
	struct s3c2410_hcd_info *info = pw;

	if (gpio_get_value(S3C2410_GPG(10)) == 0) {
		pr_debug("usb_simtec: over-current irq (oc detected)\n");
		s3c2410_usb_report_oc(info, 3);
	} else {
		pr_debug("usb_simtec: over-current irq (oc cleared)\n");
		s3c2410_usb_report_oc(info, 0);
	}

	return IRQ_HANDLED;
}

static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on)
{
	int ret;

	if (on) {
		ret = request_irq(IRQ_USBOC, usb_simtec_ocirq,
				  IRQF_DISABLED | IRQF_TRIGGER_RISING |
				   IRQF_TRIGGER_FALLING,
				  "USB Over-current", info);
		if (ret != 0) {
			printk(KERN_ERR "failed to request usb oc irq\n");
		}
	} else {
		free_irq(IRQ_USBOC, info);
	}
}

static struct s3c2410_hcd_info usb_simtec_info = {
	.port[0]	= {
		.flags	= S3C_HCDFLG_USED
	},
	.port[1]	= {
		.flags	= S3C_HCDFLG_USED
	},

	.power_control	= usb_simtec_powercontrol,
	.enable_oc	= usb_simtec_enableoc,
};


int usb_simtec_init(void)
{
	int ret;

	printk("USB Power Control, Copyright 2004 Simtec Electronics\n");

	ret = gpio_request(S3C2410_GPB(4), "USB power control");
	if (ret < 0) {
		pr_err("%s: failed to get GPB4\n", __func__);
		return ret;
	}

	ret = gpio_request(S3C2410_GPG(10), "USB overcurrent");
	if (ret < 0) {
		pr_err("%s: failed to get GPG10\n", __func__);
		gpio_free(S3C2410_GPB(4));
		return ret;
	}

	/* turn power on */
	gpio_direction_output(S3C2410_GPB(4), 1);
	gpio_direction_input(S3C2410_GPG(10));

	s3c_device_ohci.dev.platform_data = &usb_simtec_info;
	return 0;
}