linux/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2018 Marvell
   4 *
   5 * Authors:
   6 *   Evan Wang <xswang@marvell.com>
   7 *   Miquèl Raynal <miquel.raynal@bootlin.com>
   8 *
   9 * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
  10 * SMC call initial support done by Grzegorz Jaszczyk.
  11 */
  12
  13#include <linux/arm-smccc.h>
  14#include <linux/io.h>
  15#include <linux/iopoll.h>
  16#include <linux/mfd/syscon.h>
  17#include <linux/module.h>
  18#include <linux/phy.h>
  19#include <linux/phy/phy.h>
  20#include <linux/platform_device.h>
  21
  22#define MVEBU_A3700_COMPHY_LANES                3
  23#define MVEBU_A3700_COMPHY_PORTS                2
  24
  25/* COMPHY Fast SMC function identifiers */
  26#define COMPHY_SIP_POWER_ON                     0x82000001
  27#define COMPHY_SIP_POWER_OFF                    0x82000002
  28#define COMPHY_SIP_PLL_LOCK                     0x82000003
  29
  30#define COMPHY_FW_MODE_SATA                     0x1
  31#define COMPHY_FW_MODE_SGMII                    0x2
  32#define COMPHY_FW_MODE_HS_SGMII                 0x3
  33#define COMPHY_FW_MODE_USB3H                    0x4
  34#define COMPHY_FW_MODE_USB3D                    0x5
  35#define COMPHY_FW_MODE_PCIE                     0x6
  36#define COMPHY_FW_MODE_RXAUI                    0x7
  37#define COMPHY_FW_MODE_XFI                      0x8
  38#define COMPHY_FW_MODE_SFI                      0x9
  39#define COMPHY_FW_MODE_USB3                     0xa
  40
  41#define COMPHY_FW_SPEED_1_25G                   0 /* SGMII 1G */
  42#define COMPHY_FW_SPEED_2_5G                    1
  43#define COMPHY_FW_SPEED_3_125G                  2 /* SGMII 2.5G */
  44#define COMPHY_FW_SPEED_5G                      3
  45#define COMPHY_FW_SPEED_5_15625G                4 /* XFI 5G */
  46#define COMPHY_FW_SPEED_6G                      5
  47#define COMPHY_FW_SPEED_10_3125G                6 /* XFI 10G */
  48#define COMPHY_FW_SPEED_MAX                     0x3F
  49
  50#define COMPHY_FW_MODE(mode)                    ((mode) << 12)
  51#define COMPHY_FW_NET(mode, idx, speed)         (COMPHY_FW_MODE(mode) | \
  52                                                 ((idx) << 8) | \
  53                                                 ((speed) << 2))
  54#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
  55                                                 ((width) << 18))
  56
  57struct mvebu_a3700_comphy_conf {
  58        unsigned int lane;
  59        enum phy_mode mode;
  60        int submode;
  61        unsigned int port;
  62        u32 fw_mode;
  63};
  64
  65#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw)       \
  66        {                                                               \
  67                .lane = _lane,                                          \
  68                .mode = _mode,                                          \
  69                .submode = _smode,                                      \
  70                .port = _port,                                          \
  71                .fw_mode = _fw,                                         \
  72        }
  73
  74#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
  75        MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
  76
  77#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
  78        MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
  79
  80static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
  81        /* lane 0 */
  82        MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
  83                                    COMPHY_FW_MODE_USB3H),
  84        MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
  85                                    COMPHY_FW_MODE_SGMII),
  86        MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
  87                                    COMPHY_FW_MODE_HS_SGMII),
  88        /* lane 1 */
  89        MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
  90                                    COMPHY_FW_MODE_PCIE),
  91        MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
  92                                    COMPHY_FW_MODE_SGMII),
  93        MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
  94                                    COMPHY_FW_MODE_HS_SGMII),
  95        /* lane 2 */
  96        MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
  97                                    COMPHY_FW_MODE_SATA),
  98        MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
  99                                    COMPHY_FW_MODE_USB3H),
 100};
 101
 102struct mvebu_a3700_comphy_lane {
 103        struct device *dev;
 104        unsigned int id;
 105        enum phy_mode mode;
 106        int submode;
 107        int port;
 108};
 109
 110static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
 111                                  unsigned long mode)
 112{
 113        struct arm_smccc_res res;
 114
 115        arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
 116
 117        return res.a0;
 118}
 119
 120static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
 121                                          enum phy_mode mode,
 122                                          int submode)
 123{
 124        int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes);
 125
 126        /* Unused PHY mux value is 0x0 */
 127        if (mode == PHY_MODE_INVALID)
 128                return -EINVAL;
 129
 130        for (i = 0; i < n; i++) {
 131                if (mvebu_a3700_comphy_modes[i].lane == lane &&
 132                    mvebu_a3700_comphy_modes[i].port == port &&
 133                    mvebu_a3700_comphy_modes[i].mode == mode &&
 134                    mvebu_a3700_comphy_modes[i].submode == submode)
 135                        break;
 136        }
 137
 138        if (i == n)
 139                return -EINVAL;
 140
 141        return mvebu_a3700_comphy_modes[i].fw_mode;
 142}
 143
 144static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
 145                                       int submode)
 146{
 147        struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
 148        int fw_mode;
 149
 150        if (submode == PHY_INTERFACE_MODE_1000BASEX)
 151                submode = PHY_INTERFACE_MODE_SGMII;
 152
 153        fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
 154                                                 submode);
 155        if (fw_mode < 0) {
 156                dev_err(lane->dev, "invalid COMPHY mode\n");
 157                return fw_mode;
 158        }
 159
 160        /* Just remember the mode, ->power_on() will do the real setup */
 161        lane->mode = mode;
 162        lane->submode = submode;
 163
 164        return 0;
 165}
 166
 167static int mvebu_a3700_comphy_power_on(struct phy *phy)
 168{
 169        struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
 170        u32 fw_param;
 171        int fw_mode;
 172
 173        fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
 174                                                 lane->mode, lane->submode);
 175        if (fw_mode < 0) {
 176                dev_err(lane->dev, "invalid COMPHY mode\n");
 177                return fw_mode;
 178        }
 179
 180        switch (lane->mode) {
 181        case PHY_MODE_USB_HOST_SS:
 182                dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
 183                fw_param = COMPHY_FW_MODE(fw_mode);
 184                break;
 185        case PHY_MODE_SATA:
 186                dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
 187                fw_param = COMPHY_FW_MODE(fw_mode);
 188                break;
 189        case PHY_MODE_ETHERNET:
 190                switch (lane->submode) {
 191                case PHY_INTERFACE_MODE_SGMII:
 192                        dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
 193                                lane->id);
 194                        fw_param = COMPHY_FW_NET(fw_mode, lane->port,
 195                                                 COMPHY_FW_SPEED_1_25G);
 196                        break;
 197                case PHY_INTERFACE_MODE_2500BASEX:
 198                        dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
 199                                lane->id);
 200                        fw_param = COMPHY_FW_NET(fw_mode, lane->port,
 201                                                 COMPHY_FW_SPEED_3_125G);
 202                        break;
 203                default:
 204                        dev_err(lane->dev, "unsupported PHY submode (%d)\n",
 205                                lane->submode);
 206                        return -ENOTSUPP;
 207                }
 208                break;
 209        case PHY_MODE_PCIE:
 210                dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
 211                fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
 212                                          COMPHY_FW_SPEED_5G,
 213                                          phy->attrs.bus_width);
 214                break;
 215        default:
 216                dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
 217                return -ENOTSUPP;
 218        }
 219
 220        return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
 221}
 222
 223static int mvebu_a3700_comphy_power_off(struct phy *phy)
 224{
 225        struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
 226
 227        return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
 228}
 229
 230static const struct phy_ops mvebu_a3700_comphy_ops = {
 231        .power_on       = mvebu_a3700_comphy_power_on,
 232        .power_off      = mvebu_a3700_comphy_power_off,
 233        .set_mode       = mvebu_a3700_comphy_set_mode,
 234        .owner          = THIS_MODULE,
 235};
 236
 237static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
 238                                            struct of_phandle_args *args)
 239{
 240        struct mvebu_a3700_comphy_lane *lane;
 241        struct phy *phy;
 242
 243        if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
 244                return ERR_PTR(-EINVAL);
 245
 246        phy = of_phy_simple_xlate(dev, args);
 247        if (IS_ERR(phy))
 248                return phy;
 249
 250        lane = phy_get_drvdata(phy);
 251        lane->port = args->args[0];
 252
 253        return phy;
 254}
 255
 256static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
 257{
 258        struct phy_provider *provider;
 259        struct device_node *child;
 260
 261        for_each_available_child_of_node(pdev->dev.of_node, child) {
 262                struct mvebu_a3700_comphy_lane *lane;
 263                struct phy *phy;
 264                int ret;
 265                u32 lane_id;
 266
 267                ret = of_property_read_u32(child, "reg", &lane_id);
 268                if (ret < 0) {
 269                        dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
 270                                ret);
 271                        continue;
 272                }
 273
 274                if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
 275                        dev_err(&pdev->dev, "invalid 'reg' property\n");
 276                        continue;
 277                }
 278
 279                lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
 280                if (!lane)
 281                        return -ENOMEM;
 282
 283                phy = devm_phy_create(&pdev->dev, child,
 284                                      &mvebu_a3700_comphy_ops);
 285                if (IS_ERR(phy))
 286                        return PTR_ERR(phy);
 287
 288                lane->dev = &pdev->dev;
 289                lane->mode = PHY_MODE_INVALID;
 290                lane->submode = PHY_INTERFACE_MODE_NA;
 291                lane->id = lane_id;
 292                lane->port = -1;
 293                phy_set_drvdata(phy, lane);
 294        }
 295
 296        provider = devm_of_phy_provider_register(&pdev->dev,
 297                                                 mvebu_a3700_comphy_xlate);
 298        return PTR_ERR_OR_ZERO(provider);
 299}
 300
 301static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
 302        { .compatible = "marvell,comphy-a3700" },
 303        { },
 304};
 305MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
 306
 307static struct platform_driver mvebu_a3700_comphy_driver = {
 308        .probe  = mvebu_a3700_comphy_probe,
 309        .driver = {
 310                .name = "mvebu-a3700-comphy",
 311                .of_match_table = mvebu_a3700_comphy_of_match_table,
 312        },
 313};
 314module_platform_driver(mvebu_a3700_comphy_driver);
 315
 316MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
 317MODULE_DESCRIPTION("Common PHY driver for A3700");
 318MODULE_LICENSE("GPL v2");
 319