| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2024, Linaro Limited |
| */ |
| |
| #include <linux/gpio/consumer.h> |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/phy/phy.h> |
| #include <linux/regmap.h> |
| #include <linux/regulator/consumer.h> |
| |
| #define NUM_SUPPLIES 2 |
| |
| struct ptn3222 { |
| struct i2c_client *client; |
| struct phy *phy; |
| struct gpio_desc *reset_gpio; |
| struct regulator_bulk_data *supplies; |
| }; |
| |
| static int ptn3222_init(struct phy *phy) |
| { |
| struct ptn3222 *ptn3222 = phy_get_drvdata(phy); |
| int ret; |
| |
| ret = regulator_bulk_enable(NUM_SUPPLIES, ptn3222->supplies); |
| if (ret) |
| return ret; |
| |
| gpiod_set_value_cansleep(ptn3222->reset_gpio, 0); |
| |
| return 0; |
| } |
| |
| static int ptn3222_exit(struct phy *phy) |
| { |
| struct ptn3222 *ptn3222 = phy_get_drvdata(phy); |
| |
| gpiod_set_value_cansleep(ptn3222->reset_gpio, 1); |
| |
| return regulator_bulk_disable(NUM_SUPPLIES, ptn3222->supplies); |
| } |
| |
| static const struct phy_ops ptn3222_ops = { |
| .init = ptn3222_init, |
| .exit = ptn3222_exit, |
| .owner = THIS_MODULE, |
| }; |
| |
| static const struct regulator_bulk_data ptn3222_supplies[NUM_SUPPLIES] = { |
| { |
| .supply = "vdd3v3", |
| .init_load_uA = 11000, |
| }, { |
| .supply = "vdd1v8", |
| .init_load_uA = 55000, |
| } |
| }; |
| |
| static int ptn3222_probe(struct i2c_client *client) |
| { |
| struct device *dev = &client->dev; |
| struct phy_provider *phy_provider; |
| struct ptn3222 *ptn3222; |
| int ret; |
| |
| ptn3222 = devm_kzalloc(dev, sizeof(*ptn3222), GFP_KERNEL); |
| if (!ptn3222) |
| return -ENOMEM; |
| |
| ptn3222->client = client; |
| |
| ptn3222->reset_gpio = devm_gpiod_get_optional(dev, "reset", |
| GPIOD_OUT_HIGH); |
| if (IS_ERR(ptn3222->reset_gpio)) |
| return dev_err_probe(dev, PTR_ERR(ptn3222->reset_gpio), |
| "unable to acquire reset gpio\n"); |
| |
| ret = devm_regulator_bulk_get_const(dev, NUM_SUPPLIES, ptn3222_supplies, |
| &ptn3222->supplies); |
| if (ret) |
| return ret; |
| |
| ptn3222->phy = devm_phy_create(dev, dev->of_node, &ptn3222_ops); |
| if (IS_ERR(ptn3222->phy)) { |
| dev_err(dev, "failed to create PHY: %d\n", ret); |
| return PTR_ERR(ptn3222->phy); |
| } |
| |
| phy_set_drvdata(ptn3222->phy, ptn3222); |
| |
| phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
| |
| return PTR_ERR_OR_ZERO(phy_provider); |
| } |
| |
| static const struct i2c_device_id ptn3222_table[] = { |
| { "ptn3222" }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, ptn3222_table); |
| |
| static const struct of_device_id ptn3222_of_table[] = { |
| { .compatible = "nxp,ptn3222" }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, ptn3222_of_table); |
| |
| static struct i2c_driver ptn3222_driver = { |
| .driver = { |
| .name = "ptn3222", |
| .of_match_table = ptn3222_of_table, |
| }, |
| .probe = ptn3222_probe, |
| .id_table = ptn3222_table, |
| }; |
| |
| module_i2c_driver(ptn3222_driver); |
| |
| MODULE_DESCRIPTION("NXP PTN3222 eUSB2 Redriver driver"); |
| MODULE_LICENSE("GPL"); |