mirror of https://github.com/torvalds/linux.git
649 lines
16 KiB
C
649 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/array_size.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci-pwrctrl.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/unaligned.h>
|
|
|
|
#include "../pci.h"
|
|
|
|
#define TC9563_GPIO_CONFIG 0x801208
|
|
#define TC9563_RESET_GPIO 0x801210
|
|
|
|
#define TC9563_PORT_L0S_DELAY 0x82496c
|
|
#define TC9563_PORT_L1_DELAY 0x824970
|
|
|
|
#define TC9563_EMBEDDED_ETH_DELAY 0x8200d8
|
|
#define TC9563_ETH_L1_DELAY_MASK GENMASK(27, 18)
|
|
#define TC9563_ETH_L1_DELAY_VALUE(x) FIELD_PREP(TC9563_ETH_L1_DELAY_MASK, x)
|
|
#define TC9563_ETH_L0S_DELAY_MASK GENMASK(17, 13)
|
|
#define TC9563_ETH_L0S_DELAY_VALUE(x) FIELD_PREP(TC9563_ETH_L0S_DELAY_MASK, x)
|
|
|
|
#define TC9563_NFTS_2_5_GT 0x824978
|
|
#define TC9563_NFTS_5_GT 0x82497c
|
|
|
|
#define TC9563_PORT_LANE_ACCESS_ENABLE 0x828000
|
|
|
|
#define TC9563_PHY_RATE_CHANGE_OVERRIDE 0x828040
|
|
#define TC9563_PHY_RATE_CHANGE 0x828050
|
|
|
|
#define TC9563_TX_MARGIN 0x828234
|
|
|
|
#define TC9563_DFE_ENABLE 0x828a04
|
|
#define TC9563_DFE_EQ0_MODE 0x828a08
|
|
#define TC9563_DFE_EQ1_MODE 0x828a0c
|
|
#define TC9563_DFE_EQ2_MODE 0x828a14
|
|
#define TC9563_DFE_PD_MASK 0x828254
|
|
|
|
#define TC9563_PORT_SELECT 0x82c02c
|
|
#define TC9563_PORT_ACCESS_ENABLE 0x82c030
|
|
|
|
#define TC9563_POWER_CONTROL 0x82b09c
|
|
#define TC9563_POWER_CONTROL_OVREN 0x82b2c8
|
|
|
|
#define TC9563_GPIO_MASK 0xfffffff3
|
|
#define TC9563_GPIO_DEASSERT_BITS 0xc /* Bits to clear for GPIO deassert */
|
|
|
|
#define TC9563_TX_MARGIN_MIN_UA 400000
|
|
|
|
/*
|
|
* From TC9563 PORSYS rev 0.2, figure 1.1 POR boot sequence
|
|
* wait for 10ms for the internal osc frequency to stabilize.
|
|
*/
|
|
#define TC9563_OSC_STAB_DELAY_US (10 * USEC_PER_MSEC)
|
|
|
|
#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 nanoseconds */
|
|
|
|
struct tc9563_pwrctrl_reg_setting {
|
|
unsigned int offset;
|
|
unsigned int val;
|
|
};
|
|
|
|
enum tc9563_pwrctrl_ports {
|
|
TC9563_USP,
|
|
TC9563_DSP1,
|
|
TC9563_DSP2,
|
|
TC9563_DSP3,
|
|
TC9563_ETHERNET,
|
|
TC9563_MAX
|
|
};
|
|
|
|
struct tc9563_pwrctrl_cfg {
|
|
u32 l0s_delay;
|
|
u32 l1_delay;
|
|
u32 tx_amp;
|
|
u8 nfts[2]; /* GEN1 & GEN2 */
|
|
bool disable_dfe;
|
|
bool disable_port;
|
|
};
|
|
|
|
#define TC9563_PWRCTL_MAX_SUPPLY 6
|
|
|
|
static const char *const tc9563_supply_names[TC9563_PWRCTL_MAX_SUPPLY] = {
|
|
"vddc",
|
|
"vdd18",
|
|
"vdd09",
|
|
"vddio1",
|
|
"vddio2",
|
|
"vddio18",
|
|
};
|
|
|
|
struct tc9563_pwrctrl_ctx {
|
|
struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY];
|
|
struct tc9563_pwrctrl_cfg cfg[TC9563_MAX];
|
|
struct gpio_desc *reset_gpio;
|
|
struct i2c_adapter *adapter;
|
|
struct i2c_client *client;
|
|
struct pci_pwrctrl pwrctrl;
|
|
};
|
|
|
|
/*
|
|
* downstream port power off sequence, hardcoding the address
|
|
* as we don't know register names for these register offsets.
|
|
*/
|
|
static const struct tc9563_pwrctrl_reg_setting common_pwroff_seq[] = {
|
|
{0x82900c, 0x1},
|
|
{0x829010, 0x1},
|
|
{0x829018, 0x0},
|
|
{0x829020, 0x1},
|
|
{0x82902c, 0x1},
|
|
{0x829030, 0x1},
|
|
{0x82903c, 0x1},
|
|
{0x829058, 0x0},
|
|
{0x82905c, 0x1},
|
|
{0x829060, 0x1},
|
|
{0x8290cc, 0x1},
|
|
{0x8290d0, 0x1},
|
|
{0x8290d8, 0x1},
|
|
{0x8290e0, 0x1},
|
|
{0x8290e8, 0x1},
|
|
{0x8290ec, 0x1},
|
|
{0x8290f4, 0x1},
|
|
{0x82910c, 0x1},
|
|
{0x829110, 0x1},
|
|
{0x829114, 0x1},
|
|
};
|
|
|
|
static const struct tc9563_pwrctrl_reg_setting dsp1_pwroff_seq[] = {
|
|
{TC9563_PORT_ACCESS_ENABLE, 0x2},
|
|
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
|
|
{TC9563_POWER_CONTROL, 0x014f4804},
|
|
{TC9563_POWER_CONTROL_OVREN, 0x1},
|
|
{TC9563_PORT_ACCESS_ENABLE, 0x4},
|
|
};
|
|
|
|
static const struct tc9563_pwrctrl_reg_setting dsp2_pwroff_seq[] = {
|
|
{TC9563_PORT_ACCESS_ENABLE, 0x8},
|
|
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x1},
|
|
{TC9563_POWER_CONTROL, 0x014f4804},
|
|
{TC9563_POWER_CONTROL_OVREN, 0x1},
|
|
{TC9563_PORT_ACCESS_ENABLE, 0x8},
|
|
};
|
|
|
|
/*
|
|
* Since all transfers are initiated by the probe, no locks are necessary,
|
|
* as there are no concurrent calls.
|
|
*/
|
|
static int tc9563_pwrctrl_i2c_write(struct i2c_client *client,
|
|
u32 reg_addr, u32 reg_val)
|
|
{
|
|
struct i2c_msg msg;
|
|
u8 msg_buf[7];
|
|
int ret;
|
|
|
|
msg.addr = client->addr;
|
|
msg.len = 7;
|
|
msg.flags = 0;
|
|
|
|
/* Big Endian for reg addr */
|
|
put_unaligned_be24(reg_addr, &msg_buf[0]);
|
|
|
|
/* Little Endian for reg val */
|
|
put_unaligned_le32(reg_val, &msg_buf[3]);
|
|
|
|
msg.buf = msg_buf;
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
return ret == 1 ? 0 : ret;
|
|
}
|
|
|
|
static int tc9563_pwrctrl_i2c_read(struct i2c_client *client,
|
|
u32 reg_addr, u32 *reg_val)
|
|
{
|
|
struct i2c_msg msg[2];
|
|
u8 wr_data[3];
|
|
u32 rd_data;
|
|
int ret;
|
|
|
|
msg[0].addr = client->addr;
|
|
msg[0].len = 3;
|
|
msg[0].flags = 0;
|
|
|
|
/* Big Endian for reg addr */
|
|
put_unaligned_be24(reg_addr, &wr_data[0]);
|
|
|
|
msg[0].buf = wr_data;
|
|
|
|
msg[1].addr = client->addr;
|
|
msg[1].len = 4;
|
|
msg[1].flags = I2C_M_RD;
|
|
|
|
msg[1].buf = (u8 *)&rd_data;
|
|
|
|
ret = i2c_transfer(client->adapter, &msg[0], 2);
|
|
if (ret == 2) {
|
|
*reg_val = get_unaligned_le32(&rd_data);
|
|
return 0;
|
|
}
|
|
|
|
/* If only one message successfully completed, return -EIO */
|
|
return ret == 1 ? -EIO : ret;
|
|
}
|
|
|
|
static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
|
|
const struct tc9563_pwrctrl_reg_setting *seq, int len)
|
|
{
|
|
int ret, i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
ret = tc9563_pwrctrl_i2c_write(client, seq[i].offset, seq[i].val);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx,
|
|
enum tc9563_pwrctrl_ports port)
|
|
{
|
|
struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
|
|
const struct tc9563_pwrctrl_reg_setting *seq;
|
|
int ret, len;
|
|
|
|
if (!cfg->disable_port)
|
|
return 0;
|
|
|
|
if (port == TC9563_DSP1) {
|
|
seq = dsp1_pwroff_seq;
|
|
len = ARRAY_SIZE(dsp1_pwroff_seq);
|
|
} else {
|
|
seq = dsp2_pwroff_seq;
|
|
len = ARRAY_SIZE(dsp2_pwroff_seq);
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_i2c_bulk_write(ctx->client, seq, len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
|
|
common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq));
|
|
}
|
|
|
|
static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx,
|
|
enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns)
|
|
{
|
|
u32 rd_val, units;
|
|
int ret;
|
|
|
|
if (ns < TC9563_L0S_L1_DELAY_UNIT_NS)
|
|
return 0;
|
|
|
|
/* convert to units of 256ns */
|
|
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
|
|
|
|
if (port == TC9563_ETHERNET) {
|
|
ret = tc9563_pwrctrl_i2c_read(ctx->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (is_l1)
|
|
rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L1_DELAY_MASK);
|
|
else
|
|
rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK);
|
|
|
|
return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_EMBEDDED_ETH_DELAY, rd_val);
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return tc9563_pwrctrl_i2c_write(ctx->client,
|
|
is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units);
|
|
}
|
|
|
|
static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx,
|
|
enum tc9563_pwrctrl_ports port)
|
|
{
|
|
u32 amp = ctx->cfg[port].tx_amp;
|
|
int port_access;
|
|
|
|
if (amp < TC9563_TX_MARGIN_MIN_UA)
|
|
return 0;
|
|
|
|
/* txmargin = (Amp(uV) - 400000) / 3125 */
|
|
amp = (amp - TC9563_TX_MARGIN_MIN_UA) / 3125;
|
|
|
|
switch (port) {
|
|
case TC9563_USP:
|
|
port_access = 0x1;
|
|
break;
|
|
case TC9563_DSP1:
|
|
port_access = 0x2;
|
|
break;
|
|
case TC9563_DSP2:
|
|
port_access = 0x8;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct tc9563_pwrctrl_reg_setting tx_amp_seq[] = {
|
|
{TC9563_PORT_ACCESS_ENABLE, port_access},
|
|
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
|
|
{TC9563_TX_MARGIN, amp},
|
|
};
|
|
|
|
return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq));
|
|
}
|
|
|
|
static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx,
|
|
enum tc9563_pwrctrl_ports port)
|
|
{
|
|
struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
|
|
int port_access, lane_access = 0x3;
|
|
u32 phy_rate = 0x21;
|
|
|
|
if (!cfg->disable_dfe)
|
|
return 0;
|
|
|
|
switch (port) {
|
|
case TC9563_USP:
|
|
phy_rate = 0x1;
|
|
port_access = 0x1;
|
|
break;
|
|
case TC9563_DSP1:
|
|
port_access = 0x2;
|
|
break;
|
|
case TC9563_DSP2:
|
|
port_access = 0x8;
|
|
lane_access = 0x1;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct tc9563_pwrctrl_reg_setting disable_dfe_seq[] = {
|
|
{TC9563_PORT_ACCESS_ENABLE, port_access},
|
|
{TC9563_PORT_LANE_ACCESS_ENABLE, lane_access},
|
|
{TC9563_DFE_ENABLE, 0x0},
|
|
{TC9563_DFE_EQ0_MODE, 0x411},
|
|
{TC9563_DFE_EQ1_MODE, 0x11},
|
|
{TC9563_DFE_EQ2_MODE, 0x11},
|
|
{TC9563_DFE_PD_MASK, 0x7},
|
|
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x10},
|
|
{TC9563_PHY_RATE_CHANGE, phy_rate},
|
|
{TC9563_PHY_RATE_CHANGE, 0x0},
|
|
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0},
|
|
};
|
|
|
|
return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
|
|
disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq));
|
|
}
|
|
|
|
static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx,
|
|
enum tc9563_pwrctrl_ports port)
|
|
{
|
|
u8 *nfts = ctx->cfg[port].nfts;
|
|
struct tc9563_pwrctrl_reg_setting nfts_seq[] = {
|
|
{TC9563_NFTS_2_5_GT, nfts[0]},
|
|
{TC9563_NFTS_5_GT, nfts[1]},
|
|
};
|
|
int ret;
|
|
|
|
if (!nfts[0])
|
|
return 0;
|
|
|
|
ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
|
|
if (ret)
|
|
return ret;
|
|
|
|
return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, ARRAY_SIZE(nfts_seq));
|
|
}
|
|
|
|
static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, bool deassert)
|
|
{
|
|
int ret, val;
|
|
|
|
ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
|
|
|
|
return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val);
|
|
}
|
|
|
|
static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct device_node *node,
|
|
enum tc9563_pwrctrl_ports port)
|
|
{
|
|
struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
|
|
int ret;
|
|
|
|
/* Disable port if the status of the port is disabled. */
|
|
if (!of_device_is_available(node)) {
|
|
cfg->disable_port = true;
|
|
return 0;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "aspm-l0s-entry-delay-ns", &cfg->l0s_delay);
|
|
if (ret && ret != -EINVAL)
|
|
return ret;
|
|
|
|
ret = of_property_read_u32(node, "aspm-l1-entry-delay-ns", &cfg->l1_delay);
|
|
if (ret && ret != -EINVAL)
|
|
return ret;
|
|
|
|
ret = of_property_read_u32(node, "toshiba,tx-amplitude-microvolt", &cfg->tx_amp);
|
|
if (ret && ret != -EINVAL)
|
|
return ret;
|
|
|
|
ret = of_property_read_u8_array(node, "n-fts", cfg->nfts, ARRAY_SIZE(cfg->nfts));
|
|
if (ret && ret != -EINVAL)
|
|
return ret;
|
|
|
|
cfg->disable_dfe = of_property_read_bool(node, "toshiba,no-dfe-support");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx)
|
|
{
|
|
gpiod_set_value(ctx->reset_gpio, 1);
|
|
|
|
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
|
}
|
|
|
|
static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx)
|
|
{
|
|
struct tc9563_pwrctrl_cfg *cfg;
|
|
int ret, i;
|
|
|
|
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
|
if (ret < 0)
|
|
return dev_err_probe(ctx->pwrctrl.dev, ret, "cannot enable regulators\n");
|
|
|
|
gpiod_set_value(ctx->reset_gpio, 0);
|
|
|
|
fsleep(TC9563_OSC_STAB_DELAY_US);
|
|
|
|
ret = tc9563_pwrctrl_assert_deassert_reset(ctx, false);
|
|
if (ret)
|
|
goto power_off;
|
|
|
|
for (i = 0; i < TC9563_MAX; i++) {
|
|
cfg = &ctx->cfg[i];
|
|
ret = tc9563_pwrctrl_disable_port(ctx, i);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Disabling port failed\n");
|
|
goto power_off;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Setting L0s entry delay failed\n");
|
|
goto power_off;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Setting L1 entry delay failed\n");
|
|
goto power_off;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Setting Tx amplitude failed\n");
|
|
goto power_off;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_set_nfts(ctx, i);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Setting N_FTS failed\n");
|
|
goto power_off;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_disable_dfe(ctx, i);
|
|
if (ret) {
|
|
dev_err(ctx->pwrctrl.dev, "Disabling DFE failed\n");
|
|
goto power_off;
|
|
}
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_assert_deassert_reset(ctx, true);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
power_off:
|
|
tc9563_pwrctrl_power_off(ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
|
{
|
|
struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent);
|
|
struct pci_bus *bus = bridge->bus;
|
|
struct device *dev = &pdev->dev;
|
|
enum tc9563_pwrctrl_ports port;
|
|
struct tc9563_pwrctrl_ctx *ctx;
|
|
struct device_node *i2c_node;
|
|
int ret, addr;
|
|
|
|
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
|
|
ret = of_property_read_u32_index(pdev->dev.of_node, "i2c-parent", 1, &addr);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n");
|
|
|
|
i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0);
|
|
ctx->adapter = of_find_i2c_adapter_by_node(i2c_node);
|
|
of_node_put(i2c_node);
|
|
if (!ctx->adapter)
|
|
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
|
|
|
|
ctx->client = i2c_new_dummy_device(ctx->adapter, addr);
|
|
if (IS_ERR(ctx->client)) {
|
|
dev_err(dev, "Failed to create I2C client\n");
|
|
i2c_put_adapter(ctx->adapter);
|
|
return PTR_ERR(ctx->client);
|
|
}
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++)
|
|
ctx->supplies[i].supply = tc9563_supply_names[i];
|
|
|
|
ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, ctx->supplies);
|
|
if (ret) {
|
|
dev_err_probe(dev, ret, "failed to get supply regulator\n");
|
|
goto remove_i2c;
|
|
}
|
|
|
|
ctx->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH);
|
|
if (IS_ERR(ctx->reset_gpio)) {
|
|
ret = dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "failed to get resx GPIO\n");
|
|
goto remove_i2c;
|
|
}
|
|
|
|
pci_pwrctrl_init(&ctx->pwrctrl, dev);
|
|
|
|
port = TC9563_USP;
|
|
ret = tc9563_pwrctrl_parse_device_dt(ctx, pdev->dev.of_node, port);
|
|
if (ret) {
|
|
dev_err(dev, "failed to parse device tree properties: %d\n", ret);
|
|
goto remove_i2c;
|
|
}
|
|
|
|
/*
|
|
* Downstream ports are always children of the upstream port.
|
|
* The first node represents DSP1, the second node represents DSP2, and so on.
|
|
*/
|
|
for_each_child_of_node_scoped(pdev->dev.of_node, child) {
|
|
port++;
|
|
ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port);
|
|
if (ret)
|
|
break;
|
|
/* Embedded ethernet device are under DSP3 */
|
|
if (port == TC9563_DSP3) {
|
|
for_each_child_of_node_scoped(child, child1) {
|
|
port++;
|
|
ret = tc9563_pwrctrl_parse_device_dt(ctx, child1, port);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ret) {
|
|
dev_err(dev, "failed to parse device tree properties: %d\n", ret);
|
|
goto remove_i2c;
|
|
}
|
|
|
|
if (bridge->ops->assert_perst) {
|
|
ret = bridge->ops->assert_perst(bus, true);
|
|
if (ret)
|
|
goto remove_i2c;
|
|
}
|
|
|
|
ret = tc9563_pwrctrl_bring_up(ctx);
|
|
if (ret)
|
|
goto remove_i2c;
|
|
|
|
if (bridge->ops->assert_perst) {
|
|
ret = bridge->ops->assert_perst(bus, false);
|
|
if (ret)
|
|
goto power_off;
|
|
}
|
|
|
|
ret = devm_pci_pwrctrl_device_set_ready(dev, &ctx->pwrctrl);
|
|
if (ret)
|
|
goto power_off;
|
|
|
|
platform_set_drvdata(pdev, ctx);
|
|
|
|
return 0;
|
|
|
|
power_off:
|
|
tc9563_pwrctrl_power_off(ctx);
|
|
remove_i2c:
|
|
i2c_unregister_device(ctx->client);
|
|
i2c_put_adapter(ctx->adapter);
|
|
return ret;
|
|
}
|
|
|
|
static void tc9563_pwrctrl_remove(struct platform_device *pdev)
|
|
{
|
|
struct tc9563_pwrctrl_ctx *ctx = platform_get_drvdata(pdev);
|
|
|
|
tc9563_pwrctrl_power_off(ctx);
|
|
i2c_unregister_device(ctx->client);
|
|
i2c_put_adapter(ctx->adapter);
|
|
}
|
|
|
|
static const struct of_device_id tc9563_pwrctrl_of_match[] = {
|
|
{ .compatible = "pci1179,0623"},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, tc9563_pwrctrl_of_match);
|
|
|
|
static struct platform_driver tc9563_pwrctrl_driver = {
|
|
.driver = {
|
|
.name = "pwrctrl-tc9563",
|
|
.of_match_table = tc9563_pwrctrl_of_match,
|
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
|
},
|
|
.probe = tc9563_pwrctrl_probe,
|
|
.remove = tc9563_pwrctrl_remove,
|
|
};
|
|
module_platform_driver(tc9563_pwrctrl_driver);
|
|
|
|
MODULE_AUTHOR("Krishna chaitanya chundru <quic_krichai@quicinc.com>");
|
|
MODULE_DESCRIPTION("TC956x power control driver");
|
|
MODULE_LICENSE("GPL");
|