root/drivers/phy/marvell/phy-mvebu-a3700-comphy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mvebu_a3700_comphy_smc
  2. mvebu_a3700_comphy_get_fw_mode
  3. mvebu_a3700_comphy_set_mode
  4. mvebu_a3700_comphy_power_on
  5. mvebu_a3700_comphy_power_off
  6. mvebu_a3700_comphy_xlate
  7. mvebu_a3700_comphy_probe

   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 
  58 struct 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 
  81 static 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 
 103 struct 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 
 111 static 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 
 121 static 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 
 145 static 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 
 168 static 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 
 230 static 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 
 237 static 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 
 244 static 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 
 263 static 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 
 312 static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
 313         { .compatible = "marvell,comphy-a3700" },
 314         { },
 315 };
 316 MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
 317 
 318 static 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 };
 325 module_platform_driver(mvebu_a3700_comphy_driver);
 326 
 327 MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
 328 MODULE_DESCRIPTION("Common PHY driver for A3700");
 329 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */