summaryrefslogtreecommitdiff
path: root/driver/gl3590.c
blob: bde297155335aae33789c3062a294e2639a49269 (plain)
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
133
134
135
136
137
138
139
140
141
142
143
144
#include <console.h>
#include <i2c.h>
#include <system.h>
#include <util.h>

#include <gl3590.h>

/* GL3590 is unique in terms of i2c_read, since it doesn't support repeated
 * start sequence. One need to issue two separate transactions - first is write
 * with a register offset, then after a delay second transaction is actual read.
 */
int gl3590_read(int hub, uint8_t reg, uint8_t *data, int count)
{
	int rv;
	struct uhub_i2c_iface_t *uhub_p = &uhub_config[hub];

	i2c_lock(uhub_p->i2c_host_port, 1);
	rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
			       uhub_p->i2c_addr,
			       &reg, 1,
			       NULL, 0,
			       I2C_XFER_SINGLE);
	i2c_lock(uhub_p->i2c_host_port, 0);

	if (rv)
		return rv;

	/* GL3590 requires at least 300us before data is ready */
	udelay(400);

	i2c_lock(uhub_p->i2c_host_port, 1);
	rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
			       uhub_p->i2c_addr,
			       NULL, 0,
			       data, count,
			       I2C_XFER_SINGLE);
	i2c_lock(uhub_p->i2c_host_port, 0);

	return rv;
};

int gl3590_write(int hub, uint8_t reg, uint8_t *data, int count)
{
	int rv;
	uint8_t buf[5];
	struct uhub_i2c_iface_t *uhub_p = &uhub_config[hub];

	/* GL3590 registers accept 4 bytes at max */
	if (count > (sizeof(buf) - 1)) {
		ccprintf("Too many bytes to write");
		return EC_ERROR_INVAL;
	}

	buf[0] = reg;
	memcpy(&buf[1], data, count);

	i2c_lock(uhub_p->i2c_host_port, 1);
	rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
			       uhub_p->i2c_addr,
			       buf, count + 1,
			       NULL, 0,
			       I2C_XFER_SINGLE);
	i2c_lock(uhub_p->i2c_host_port, 0);

	return rv;
}

void gl3590_irq_handler(int hub)
{
	uint8_t buf = 0;
	uint8_t res_reg[2];

	/* Verify that irq is pending */
	if (gl3590_read(hub, GL3590_INT_REG, &buf, sizeof(buf))) {
		ccprintf("Cannot read from the host hub i2c\n");
		goto exit;
	}

	if ((buf & GL3590_INT_PENDING) == 0) {
		ccprintf("Invalid hub event\n");
		goto exit;
	}

	/* Get the hub event reason */
	if (gl3590_read(hub, GL3590_RESPONSE_REG, res_reg, sizeof(res_reg))) {
		ccprintf("Cannot read from the host hub i2c\n");
		goto exit;
	}

	if ((res_reg[0] & GL3590_RESPONSE_REG_SYNC_MASK) == 0)
		ccprintf("Host hub response: ");
	else
		ccprintf("Host hub event! ");

	switch(res_reg[0]) {
	case 0x0:
		ccprintf("No response");
		break;
	case 0x1:
		ccprintf("Successful");
		break;
	case 0x2:
		ccprintf("Invalid command");
		break;
	case 0x3:
		ccprintf("Invalid arguments");
		break;
	case 0x4:
		ccprintf("Invalid port: %d", res_reg[1]);
		break;
	case 0x5:
		ccprintf("Command not completed");
		break;
	case 0x80:
		ccprintf("Reset complete");
		break;
	case 0x81:
		ccprintf("Power operation mode change");
		break;
	case 0x82:
		ccprintf("Connect change");
		break;
	case 0x83:
		ccprintf("Error on the specific port");
		break;
	case 0x84:
		ccprintf("Hub state change");
		break;
	case 0x85:
		ccprintf("SetFeature PORT_POWER failure");
		break;
	default:
		ccprintf("Unknown value: 0x%0x", res_reg[0]);
	}
	ccprintf("\n");

	if (res_reg[1])
		ccprintf("Affected port %d\n", res_reg[1]);

exit:
	/* Try to clear interrupt */
	buf = GL3590_INT_CLEAR;
	gl3590_write(hub, GL3590_INT_REG, &buf, sizeof(buf));
}