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