summaryrefslogtreecommitdiff
path: root/driver/usb_mux/amd_fp5.c
blob: a912d03365f19d480c9db775c83d2343f4087bc7 (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
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * AMD FP5 USB/DP Mux.
 */

#include "amd_fp5.h"
#include "chipset.h"
#include "common.h"
#include "i2c.h"
#include "usb_mux.h"

static inline int amd_fp5_mux_read(int port, uint8_t *val)
{
	uint8_t buf[3] = { 0 };
	int rv;

	rv = i2c_xfer(I2C_PORT_USB_MUX, AMD_FP5_MUX_I2C_ADDR_FLAGS,
		      NULL, 0, buf, 3);
	if (rv)
		return rv;

	*val = buf[port + 1];

	return EC_SUCCESS;
}

static inline int amd_fp5_mux_write(int port, uint8_t val)
{
	return i2c_write8(I2C_PORT_USB_MUX, AMD_FP5_MUX_I2C_ADDR_FLAGS,
			  port, val);
}

static int amd_fp5_init(int port)
{
	return EC_SUCCESS;
}

static int amd_fp5_set_mux(int port, mux_state_t mux_state)
{
	uint8_t val = 0;

	/*
	 * This MUX is on the FP5 SoC.  If that device is not powered then
	 * we either have to complain that it is not powered or if we were
	 * setting the state to OFF, then go ahead and report that we did
	 * it because a powered down MUX is off.
	 */
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		return (mux_state == TYPEC_MUX_NONE)
			? EC_SUCCESS
			: EC_ERROR_NOT_POWERED;

	if ((mux_state & MUX_USB_ENABLED) && (mux_state & MUX_DP_ENABLED))
		val = (mux_state & MUX_POLARITY_INVERTED)
			? AMD_FP5_MUX_DOCK_INVERTED : AMD_FP5_MUX_DOCK;
	else if (mux_state & MUX_USB_ENABLED)
		val = (mux_state & MUX_POLARITY_INVERTED)
			? AMD_FP5_MUX_USB_INVERTED : AMD_FP5_MUX_USB;
	else if (mux_state & MUX_DP_ENABLED)
		val = (mux_state & MUX_POLARITY_INVERTED)
			? AMD_FP5_MUX_DP_INVERTED : AMD_FP5_MUX_DP;

	return amd_fp5_mux_write(port, val);
}

static int amd_fp5_get_mux(int port, mux_state_t *mux_state)
{
	uint8_t val = AMD_FP5_MUX_SAFE;

	/*
	 * This MUX is on the FP5 SoC.  Only access the device if we
	 * have power.  If that device is not powered then claim the
	 * state to be NONE, which is SAFE.
	 */
	if (!chipset_in_state(CHIPSET_STATE_HARD_OFF)) {
		int rv;

		rv = amd_fp5_mux_read(port, &val);
		if (rv)
			return rv;
	}


	switch (val) {
	case AMD_FP5_MUX_USB:
		*mux_state = MUX_USB_ENABLED;
		break;
	case AMD_FP5_MUX_USB_INVERTED:
		*mux_state = MUX_USB_ENABLED | MUX_POLARITY_INVERTED;
		break;
	case AMD_FP5_MUX_DOCK:
		*mux_state = MUX_USB_ENABLED | MUX_DP_ENABLED;
		break;
	case AMD_FP5_MUX_DOCK_INVERTED:
		*mux_state = MUX_USB_ENABLED | MUX_DP_ENABLED
			     | MUX_POLARITY_INVERTED;
		break;
	case AMD_FP5_MUX_DP:
		*mux_state = MUX_DP_ENABLED;
		break;
	case AMD_FP5_MUX_DP_INVERTED:
		*mux_state = MUX_DP_ENABLED | MUX_POLARITY_INVERTED;
		break;
	case AMD_FP5_MUX_SAFE:
	default:
		*mux_state = TYPEC_MUX_NONE;
		break;
	}

	return EC_SUCCESS;
}

const struct usb_mux_driver amd_fp5_usb_mux_driver = {
	.init = amd_fp5_init,
	.set = amd_fp5_set_mux,
	.get = amd_fp5_get_mux,
};