blob: 92c7e682a4c03ff636e522ebe25d7b0b7d578beb [file] [log] [blame]
/* 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 "hooks.h"
#include "i2c.h"
#include "queue.h"
#include "timer.h"
#include "usb_mux.h"
static mux_state_t saved_mux_state[CONFIG_USB_PD_PORT_MAX_COUNT];
static inline int amd_fp5_mux_read(const struct usb_mux *me, uint8_t *val)
{
uint8_t buf[3] = { 0 };
int rv;
rv = i2c_xfer(me->i2c_port, me->i2c_addr_flags,
NULL, 0, buf, 3);
if (rv)
return rv;
*val = buf[me->usb_port + 1];
return EC_SUCCESS;
}
static inline int amd_fp5_mux_write(const struct usb_mux *me, uint8_t val)
{
return i2c_write8(me->i2c_port, me->i2c_addr_flags,
me->usb_port, val);
}
static int amd_fp5_init(const struct usb_mux *me)
{
return EC_SUCCESS;
}
static int amd_fp5_set_mux(const struct usb_mux *me, mux_state_t mux_state)
{
uint8_t val = 0;
saved_mux_state[me->usb_port] = mux_state;
/*
* 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 == USB_PD_MUX_NONE)
? EC_SUCCESS
: EC_ERROR_NOT_POWERED;
if ((mux_state & USB_PD_MUX_USB_ENABLED) &&
(mux_state & USB_PD_MUX_DP_ENABLED))
val = (mux_state & USB_PD_MUX_POLARITY_INVERTED)
? AMD_FP5_MUX_DOCK_INVERTED : AMD_FP5_MUX_DOCK;
else if (mux_state & USB_PD_MUX_USB_ENABLED)
val = (mux_state & USB_PD_MUX_POLARITY_INVERTED)
? AMD_FP5_MUX_USB_INVERTED : AMD_FP5_MUX_USB;
else if (mux_state & USB_PD_MUX_DP_ENABLED)
val = (mux_state & USB_PD_MUX_POLARITY_INVERTED)
? AMD_FP5_MUX_DP_INVERTED : AMD_FP5_MUX_DP;
return amd_fp5_mux_write(me, val);
}
static int amd_fp5_get_mux(const struct usb_mux *me, 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(me, &val);
if (rv)
return rv;
}
switch (val) {
case AMD_FP5_MUX_USB:
*mux_state = USB_PD_MUX_USB_ENABLED;
break;
case AMD_FP5_MUX_USB_INVERTED:
*mux_state = USB_PD_MUX_USB_ENABLED |
USB_PD_MUX_POLARITY_INVERTED;
break;
case AMD_FP5_MUX_DOCK:
*mux_state = USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED;
break;
case AMD_FP5_MUX_DOCK_INVERTED:
*mux_state = USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED
| USB_PD_MUX_POLARITY_INVERTED;
break;
case AMD_FP5_MUX_DP:
*mux_state = USB_PD_MUX_DP_ENABLED;
break;
case AMD_FP5_MUX_DP_INVERTED:
*mux_state = USB_PD_MUX_DP_ENABLED |
USB_PD_MUX_POLARITY_INVERTED;
break;
case AMD_FP5_MUX_SAFE:
default:
*mux_state = USB_PD_MUX_NONE;
break;
}
return EC_SUCCESS;
}
static struct queue const chipset_reset_queue
= QUEUE_NULL(CONFIG_USB_PD_PORT_MAX_COUNT, struct usb_mux *);
static void amd_fp5_chipset_reset_delay(void)
{
struct usb_mux *me;
int rv;
while (queue_remove_unit(&chipset_reset_queue, &me)) {
rv = amd_fp5_set_mux(me, saved_mux_state[me->usb_port]);
if (rv)
ccprints("C%d restore mux rv:%d", me->usb_port, rv);
}
}
DECLARE_DEFERRED(amd_fp5_chipset_reset_delay);
/*
* The AP's internal USB-C mux is reset when AP resets, so wait for
* it to be ready and then restore the previous setting.
*/
static int amd_fp5_chipset_reset(const struct usb_mux *me)
{
queue_add_unit(&chipset_reset_queue, &me);
hook_call_deferred(&amd_fp5_chipset_reset_delay_data, 200 * MSEC);
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,
.chipset_reset = &amd_fp5_chipset_reset,
};