linux/drivers/usb/typec/tipd/core.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Driver for TI TPS6598x USB Power Delivery controller family
 *
 * Copyright (C) 2017, Intel Corporation
 * Author: Heikki Krogerus <[email protected]>
 */

#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>
#include <linux/usb/typec.h>
#include <linux/usb/typec_altmode.h>
#include <linux/usb/role.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>

#include "tps6598x.h"
#include "trace.h"

/* Register offsets */
#define TPS_REG_VID
#define TPS_REG_MODE
#define TPS_REG_CMD1
#define TPS_REG_DATA1
#define TPS_REG_VERSION
#define TPS_REG_INT_EVENT1
#define TPS_REG_INT_EVENT2
#define TPS_REG_INT_MASK1
#define TPS_REG_INT_MASK2
#define TPS_REG_INT_CLEAR1
#define TPS_REG_INT_CLEAR2
#define TPS_REG_SYSTEM_POWER_STATE
#define TPS_REG_STATUS
#define TPS_REG_SYSTEM_CONF
#define TPS_REG_CTRL_CONF
#define TPS_REG_BOOT_STATUS
#define TPS_REG_POWER_STATUS
#define TPS_REG_PD_STATUS
#define TPS_REG_RX_IDENTITY_SOP
#define TPS_REG_DATA_STATUS
#define TPS_REG_SLEEP_CONF

/* TPS_REG_SYSTEM_CONF bits */
#define TPS_SYSCONF_PORTINFO(c)

/*
 * BPMs task timeout, recommended 5 seconds
 * pg.48 TPS2575 Host Interface Technical Reference
 * Manual (Rev. A)
 * https://www.ti.com/lit/ug/slvuc05a/slvuc05a.pdf
 */
#define TPS_BUNDLE_TIMEOUT

/* BPMs return code */
#define TPS_TASK_BPMS_INVALID_BUNDLE_SIZE
#define TPS_TASK_BPMS_INVALID_SLAVE_ADDR
#define TPS_TASK_BPMS_INVALID_TIMEOUT

/* PBMc data out */
#define TPS_PBMC_RC
#define TPS_PBMC_DPCS

/* reset de-assertion to ready for operation */
#define TPS_SETUP_MS

enum {};

/* TPS_REG_RX_IDENTITY_SOP */
struct tps6598x_rx_identity_reg {} __packed;

/* Standard Task return codes */
#define TPS_TASK_TIMEOUT
#define TPS_TASK_REJECTED

enum {};

static const char *const modes[] =;

/* Unrecognized commands will be replaced with "!CMD" */
#define INVALID_CMD(_cmd_)

struct tps6598x;

struct tipd_data {};

struct tps6598x {};

static enum power_supply_property tps6598x_psy_props[] =;

static enum power_supply_usb_type tps6598x_psy_usb_types[] =;

static const char *tps6598x_psy_name_prefix =;

/*
 * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2:
 * https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf
 */
#define TPS_MAX_LEN

static int
tps6598x_block_read(struct tps6598x *tps, u8 reg, void *val, size_t len)
{}

static int tps6598x_block_write(struct tps6598x *tps, u8 reg,
				const void *val, size_t len)
{}

static inline int tps6598x_read8(struct tps6598x *tps, u8 reg, u8 *val)
{}

static inline int tps6598x_read16(struct tps6598x *tps, u8 reg, u16 *val)
{}

static inline int tps6598x_read32(struct tps6598x *tps, u8 reg, u32 *val)
{}

static inline int tps6598x_read64(struct tps6598x *tps, u8 reg, u64 *val)
{}

static inline int tps6598x_write8(struct tps6598x *tps, u8 reg, u8 val)
{}

static inline int tps6598x_write64(struct tps6598x *tps, u8 reg, u64 val)
{}

static inline int
tps6598x_write_4cc(struct tps6598x *tps, u8 reg, const char *val)
{}

static int tps6598x_read_partner_identity(struct tps6598x *tps)
{}

static void tps6598x_set_data_role(struct tps6598x *tps,
				   enum typec_data_role role, bool connected)
{}

static int tps6598x_connect(struct tps6598x *tps, u32 status)
{}

static void tps6598x_disconnect(struct tps6598x *tps, u32 status)
{}

static int tps6598x_exec_cmd_tmo(struct tps6598x *tps, const char *cmd,
			     size_t in_len, const u8 *in_data,
			     size_t out_len, u8 *out_data,
			     u32 cmd_timeout_ms, u32 res_delay_ms)
{}

static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd,
			     size_t in_len, const u8 *in_data,
			     size_t out_len, u8 *out_data)
{}

static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role)
{}

static int tps6598x_pr_set(struct typec_port *port, enum typec_role role)
{}

static const struct typec_operations tps6598x_ops =;

static bool tps6598x_read_status(struct tps6598x *tps, u32 *status)
{}

static bool tps6598x_read_data_status(struct tps6598x *tps)
{}

static bool tps6598x_read_power_status(struct tps6598x *tps)
{}

static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status)
{}

static irqreturn_t cd321x_interrupt(int irq, void *data)
{}

static bool tps6598x_has_role_changed(struct tps6598x *tps, u32 status)
{}

static irqreturn_t tps25750_interrupt(int irq, void *data)
{}

static irqreturn_t tps6598x_interrupt(int irq, void *data)
{}

/* Time interval for Polling */
#define POLL_INTERVAL
static void tps6598x_poll_work(struct work_struct *work)
{}

static int tps6598x_check_mode(struct tps6598x *tps)
{}

static const struct regmap_config tps6598x_regmap_config =;

static int tps6598x_psy_get_online(struct tps6598x *tps,
				   union power_supply_propval *val)
{}

static int tps6598x_psy_get_prop(struct power_supply *psy,
				 enum power_supply_property psp,
				 union power_supply_propval *val)
{}

static int cd321x_switch_power_state(struct tps6598x *tps, u8 target_state)
{}

static int devm_tps6598_psy_register(struct tps6598x *tps)
{}

static int
tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
{}

static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw,
				const char **firmware_name)
{}

static int
tps25750_write_firmware(struct tps6598x *tps,
			u8 bpms_addr, const u8 *data, size_t len)
{}

static int
tps25750_exec_pbms(struct tps6598x *tps, u8 *in_data, size_t in_len)
{}

static int tps25750_abort_patch_process(struct tps6598x *tps)
{}

static int tps25750_start_patch_burst_mode(struct tps6598x *tps)
{}

static int tps25750_complete_patch_process(struct tps6598x *tps)
{}

static int tps25750_apply_patch(struct tps6598x *tps)
{
	int ret;
	unsigned long timeout;
	u64 status = 0;

	ret = tps6598x_block_read(tps, TPS_REG_BOOT_STATUS, &status, 5);
	if (ret)
		return ret;
	/*
	 * Nothing to be done if the configuration
	 * is being loaded from EERPOM
	 */
	if (status & TPS_BOOT_STATUS_I2C_EEPROM_PRESENT)
		goto wait_for_app;

	ret = tps25750_start_patch_burst_mode(tps);
	if (ret) {
		tps25750_abort_patch_process(tps);
		return ret;
	}

	ret = tps25750_complete_patch_process(tps);
	if (ret)
		return ret;

wait_for_app:
	timeout = jiffies + msecs_to_jiffies(1000);

	do {
		ret = tps6598x_check_mode(tps);
		if (ret < 0)
			return ret;

		if (time_is_before_jiffies(timeout))
			return -ETIMEDOUT;

	} while (ret != TPS_MODE_APP);

	/*
	 * The dead battery flag may be triggered when the controller
	 * port is connected to a device that can source power and
	 * attempts to power up both the controller and the board it is on.
	 * To restore controller functionality, it is necessary to clear
	 * this flag
	 */
	if (status & TPS_BOOT_STATUS_DEAD_BATTERY_FLAG) {
		ret = tps6598x_exec_cmd(tps, "DBfg", 0, NULL, 0, NULL);
		if (ret) {
			dev_err(tps->dev, "failed to clear dead battery %d\n", ret);
			return ret;
		}
	}

	dev_info(tps->dev, "controller switched to \"APP\" mode\n");

	return 0;
};

static int tps6598x_apply_patch(struct tps6598x *tps)
{
	u8 in = TPS_PTCS_CONTENT_DEV | TPS_PTCS_CONTENT_APP;
	u8 out[TPS_MAX_LEN] = {0};
	size_t in_len = sizeof(in);
	size_t copied_bytes = 0;
	size_t bytes_left;
	const struct firmware *fw;
	const char *firmware_name;
	int ret;

	ret = tps_request_firmware(tps, &fw, &firmware_name);
	if (ret)
		return ret;

	ret = tps6598x_exec_cmd(tps, "PTCs", in_len, &in,
				TPS_PTCS_OUT_BYTES, out);
	if (ret || out[TPS_PTCS_STATUS] == TPS_PTCS_STATUS_FAIL) {
		if (!ret)
			ret = -EBUSY;
		dev_err(tps->dev, "Update start failed (%d)\n", ret);
		goto release_fw;
	}

	bytes_left = fw->size;
	while (bytes_left) {
		in_len = min(bytes_left, TPS_MAX_LEN);
		ret = tps6598x_exec_cmd(tps, "PTCd", in_len,
					fw->data + copied_bytes,
					TPS_PTCD_OUT_BYTES, out);
		if (ret || out[TPS_PTCD_TRANSFER_STATUS] ||
		    out[TPS_PTCD_LOADING_STATE] == TPS_PTCD_LOAD_ERR) {
			if (!ret)
				ret = -EBUSY;
			dev_err(tps->dev, "Patch download failed (%d)\n", ret);
			goto release_fw;
		}
		copied_bytes += in_len;
		bytes_left -= in_len;
	}

	ret = tps6598x_exec_cmd(tps, "PTCc", 0, NULL, TPS_PTCC_OUT_BYTES, out);
	if (ret || out[TPS_PTCC_DEV] || out[TPS_PTCC_APP]) {
		if (!ret)
			ret = -EBUSY;
		dev_err(tps->dev, "Update completion failed (%d)\n", ret);
		goto release_fw;
	}
	msleep(TPS_SETUP_MS);
	dev_info(tps->dev, "Firmware update succeeded\n");

release_fw:
	release_firmware(fw);
	if (ret) {
		dev_err(tps->dev, "Failed to write patch %s of %zu bytes\n",
			firmware_name, fw->size);
	}

	return ret;
};

static int cd321x_init(struct tps6598x *tps)
{}

static int tps25750_init(struct tps6598x *tps)
{}

static int tps6598x_init(struct tps6598x *tps)
{}

static int cd321x_reset(struct tps6598x *tps)
{}

static int tps25750_reset(struct tps6598x *tps)
{}

static int tps6598x_reset(struct tps6598x *tps)
{}

static int
tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
{}

static int tps6598x_probe(struct i2c_client *client)
{}

static void tps6598x_remove(struct i2c_client *client)
{}

static int __maybe_unused tps6598x_suspend(struct device *dev)
{}

static int __maybe_unused tps6598x_resume(struct device *dev)
{}

static const struct dev_pm_ops tps6598x_pm_ops =;

static const struct tipd_data cd321x_data =;

static const struct tipd_data tps6598x_data =;

static const struct tipd_data tps25750_data =;

static const struct of_device_id tps6598x_of_match[] =;
MODULE_DEVICE_TABLE(of, tps6598x_of_match);

static const struct i2c_device_id tps6598x_id[] =;
MODULE_DEVICE_TABLE(i2c, tps6598x_id);

static struct i2c_driver tps6598x_i2c_driver =;
module_i2c_driver();

MODULE_AUTHOR();
MODULE_LICENSE();
MODULE_DESCRIPTION();