Commit affc804c authored by Prashant Malani's avatar Prashant Malani

platform/chrome: cros_typec_switch: Add switch driver

Introduce a driver to configure USB Type-C mode switches and retimers
which are controlled by the ChromeOS EC (Embedded Controller).
This allows Type-C port drivers, as well as alternate mode drivers to
configure their relevant mode switches and retimers according to the
Type-C state they want to achieve.

ACPI devices with ID GOOG001A will bind to this driver.

Currently, we only register a retimer switch with a stub set function.
Subsequent patches will implement the host command set functionality,
and introduce mode switches.
Signed-off-by: default avatarPrashant Malani <pmalani@chromium.org>
Reviewed-by: default avatarTzung-Bi Shih <tzungbi@kernel.org>
Link: https://lore.kernel.org/r/20220816214857.2088914-3-pmalani@chromium.org
parent 77947238
...@@ -4894,6 +4894,7 @@ M: Prashant Malani <pmalani@chromium.org> ...@@ -4894,6 +4894,7 @@ M: Prashant Malani <pmalani@chromium.org>
L: chrome-platform@lists.linux.dev L: chrome-platform@lists.linux.dev
S: Maintained S: Maintained
F: drivers/platform/chrome/cros_ec_typec.c F: drivers/platform/chrome/cros_ec_typec.c
F: drivers/platform/chrome/cros_typec_switch.c
CHROMEOS EC USB PD NOTIFY DRIVER CHROMEOS EC USB PD NOTIFY DRIVER
M: Prashant Malani <pmalani@chromium.org> M: Prashant Malani <pmalani@chromium.org>
......
...@@ -265,6 +265,17 @@ config CHROMEOS_PRIVACY_SCREEN ...@@ -265,6 +265,17 @@ config CHROMEOS_PRIVACY_SCREEN
this should probably always be built into the kernel to avoid or this should probably always be built into the kernel to avoid or
minimize drm probe deferral. minimize drm probe deferral.
config CROS_TYPEC_SWITCH
tristate "ChromeOS EC Type-C Switch Control"
depends on MFD_CROS_EC_DEV && TYPEC && ACPI
default MFD_CROS_EC_DEV
help
If you say Y here, you get support for configuring the ChromeOS EC Type-C
muxes and retimers.
To compile this driver as a module, choose M here: the module will be
called cros_typec_switch.
source "drivers/platform/chrome/wilco_ec/Kconfig" source "drivers/platform/chrome/wilco_ec/Kconfig"
# Kunit test cases # Kunit test cases
......
...@@ -12,6 +12,7 @@ obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o ...@@ -12,6 +12,7 @@ obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o
obj-$(CONFIG_CROS_EC) += cros_ec.o obj-$(CONFIG_CROS_EC) += cros_ec.o
obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o
obj-$(CONFIG_CROS_TYPEC_SWITCH) += cros_typec_switch.o
obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022 Google LLC
*
* This driver provides the ability to configure Type-C muxes and retimers which are controlled by
* the ChromeOS EC.
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_device.h>
#include <linux/usb/typec_retimer.h>
#define DRV_NAME "cros-typec-switch"
/* Handles and other relevant data required for each port's switches. */
struct cros_typec_port {
int port_num;
struct typec_retimer *retimer;
struct cros_typec_switch_data *sdata;
};
/* Driver-specific data. */
struct cros_typec_switch_data {
struct device *dev;
struct cros_ec_device *ec;
struct cros_typec_port *ports[EC_USB_PD_MAX_PORTS];
};
static int cros_typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
return 0;
}
static void cros_typec_unregister_switches(struct cros_typec_switch_data *sdata)
{
int i;
for (i = 0; i < EC_USB_PD_MAX_PORTS; i++) {
if (!sdata->ports[i])
continue;
typec_retimer_unregister(sdata->ports[i]->retimer);
}
}
static int cros_typec_register_retimer(struct cros_typec_port *port, struct fwnode_handle *fwnode)
{
struct typec_retimer_desc retimer_desc = {
.fwnode = fwnode,
.drvdata = port,
.name = fwnode_get_name(fwnode),
.set = cros_typec_retimer_set,
};
port->retimer = typec_retimer_register(port->sdata->dev, &retimer_desc);
if (IS_ERR(port->retimer))
return PTR_ERR(port->retimer);
return 0;
}
static int cros_typec_register_switches(struct cros_typec_switch_data *sdata)
{
struct cros_typec_port *port;
struct device *dev = sdata->dev;
struct fwnode_handle *fwnode;
struct acpi_device *adev;
unsigned long long index;
int nports, ret;
nports = device_get_child_node_count(dev);
if (nports == 0) {
dev_err(dev, "No switch devices found.\n");
return -ENODEV;
}
device_for_each_child_node(dev, fwnode) {
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port) {
ret = -ENOMEM;
goto err_switch;
}
adev = to_acpi_device_node(fwnode);
if (!adev) {
dev_err(fwnode->dev, "Couldn't get ACPI device handle\n");
ret = -ENODEV;
goto err_switch;
}
ret = acpi_evaluate_integer(adev->handle, "_ADR", NULL, &index);
if (ACPI_FAILURE(ret)) {
dev_err(fwnode->dev, "_ADR wasn't evaluated\n");
ret = -ENODATA;
goto err_switch;
}
if (index < 0 || index >= EC_USB_PD_MAX_PORTS) {
dev_err(fwnode->dev, "Invalid port index number: %llu", index);
ret = -EINVAL;
goto err_switch;
}
port->sdata = sdata;
port->port_num = index;
sdata->ports[index] = port;
ret = cros_typec_register_retimer(port, fwnode);
if (ret) {
dev_err(dev, "Retimer switch register failed\n");
goto err_switch;
}
dev_dbg(dev, "Retimer switch registered for index %llu\n", index);
}
return 0;
err_switch:
cros_typec_unregister_switches(sdata);
return ret;
}
static int cros_typec_switch_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_typec_switch_data *sdata;
sdata = devm_kzalloc(dev, sizeof(*sdata), GFP_KERNEL);
if (!sdata)
return -ENOMEM;
sdata->dev = dev;
sdata->ec = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, sdata);
return cros_typec_register_switches(sdata);
}
static int cros_typec_switch_remove(struct platform_device *pdev)
{
struct cros_typec_switch_data *sdata = platform_get_drvdata(pdev);
cros_typec_unregister_switches(sdata);
return 0;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id cros_typec_switch_acpi_id[] = {
{ "GOOG001A", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, cros_typec_switch_acpi_id);
#endif
static struct platform_driver cros_typec_switch_driver = {
.driver = {
.name = DRV_NAME,
.acpi_match_table = ACPI_PTR(cros_typec_switch_acpi_id),
},
.probe = cros_typec_switch_probe,
.remove = cros_typec_switch_remove,
};
module_platform_driver(cros_typec_switch_driver);
MODULE_AUTHOR("Prashant Malani <pmalani@chromium.org>");
MODULE_DESCRIPTION("ChromeOS EC Type-C Switch control");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment