^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2014 STMicroelectronics
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * STMicroelectronics Generic PHY driver for STiH407 USB2.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/reset.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/mfd/syscon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/phy/phy.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define PHYPARAM_REG 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define PHYCTRL_REG 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) /* Default PHY_SEL and REFCLKSEL configuration */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) /* ports parameters overriding */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct stih407_usb2_picophy {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) struct phy *phy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct regmap *regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct device *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) struct reset_control *rstc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct reset_control *rstport;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) int ctrl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) int param;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) reset_control_deassert(phy_dev->rstc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) STIH407_USB_PICOPHY_CTRL_PORT_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) STIH407_USB_PICOPHY_CTRL_PORT_CONF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) static int stih407_usb2_init_port(struct phy *phy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) stih407_usb2_pico_ctrl(phy_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) ret = regmap_update_bits(phy_dev->regmap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) phy_dev->param,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) STIH407_USB_PICOPHY_PARAM_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) STIH407_USB_PICOPHY_PARAM_DEF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) return reset_control_deassert(phy_dev->rstport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static int stih407_usb2_exit_port(struct phy *phy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * Only port reset is asserted, phy global reset is kept untouched
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * as other ports may still be active. When all ports are in reset
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) * state, assumption is made that power will be cut off on the phy, in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) * case of suspend for instance. Theoretically, asserting individual
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) * reset (like here) or global reset should be equivalent.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) return reset_control_assert(phy_dev->rstport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static const struct phy_ops stih407_usb2_picophy_data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) .init = stih407_usb2_init_port,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) .exit = stih407_usb2_exit_port,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) static int stih407_usb2_picophy_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct stih407_usb2_picophy *phy_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct device_node *np = dev->of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) struct phy_provider *phy_provider;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) struct phy *phy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (!phy_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) phy_dev->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) dev_set_drvdata(dev, phy_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) phy_dev->rstc = devm_reset_control_get_shared(dev, "global");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (IS_ERR(phy_dev->rstc)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) dev_err(dev, "failed to ctrl picoPHY reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return PTR_ERR(phy_dev->rstc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (IS_ERR(phy_dev->rstport)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) dev_err(dev, "failed to ctrl picoPHY reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return PTR_ERR(phy_dev->rstport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /* Reset port by default: only deassert it in phy init */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) reset_control_assert(phy_dev->rstport);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) if (IS_ERR(phy_dev->regmap)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) dev_err(dev, "No syscfg phandle specified\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) return PTR_ERR(phy_dev->regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) &phy_dev->param);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) dev_err(dev, "can't get phyparam offset (%d)\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) &phy_dev->ctrl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) dev_err(dev, "can't get phyctrl offset (%d)\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) if (IS_ERR(phy)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) dev_err(dev, "failed to create Display Port PHY\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return PTR_ERR(phy);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) phy_dev->phy = phy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) phy_set_drvdata(phy, phy_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (IS_ERR(phy_provider))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return PTR_ERR(phy_provider);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) dev_info(dev, "STiH407 USB Generic picoPHY driver probed!");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) static const struct of_device_id stih407_usb2_picophy_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) { .compatible = "st,stih407-usb2-phy" },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) { /*sentinel */ },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static struct platform_driver stih407_usb2_picophy_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) .probe = stih407_usb2_picophy_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) .name = "stih407-usb-genphy",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) .of_match_table = stih407_usb2_picophy_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) module_platform_driver(stih407_usb2_picophy_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) MODULE_LICENSE("GPL v2");