Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Microchip KSZ8863 series register access through SMI
0004  *
0005  * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de>
0006  */
0007 
0008 #include "ksz8.h"
0009 #include "ksz_common.h"
0010 
0011 /* Serial Management Interface (SMI) uses the following frame format:
0012  *
0013  *       preamble|start|Read/Write|  PHY   |  REG  |TA|   Data bits      | Idle
0014  *               |frame| OP code  |address |address|  |                  |
0015  * read | 32x1´s | 01  |    00    | 1xRRR  | RRRRR |Z0| 00000000DDDDDDDD |  Z
0016  * write| 32x1´s | 01  |    00    | 0xRRR  | RRRRR |10| xxxxxxxxDDDDDDDD |  Z
0017  *
0018  */
0019 
0020 #define SMI_KSZ88XX_READ_PHY    BIT(4)
0021 
0022 static int ksz8863_mdio_read(void *ctx, const void *reg_buf, size_t reg_len,
0023                  void *val_buf, size_t val_len)
0024 {
0025     struct ksz_device *dev = ctx;
0026     struct mdio_device *mdev;
0027     u8 reg = *(u8 *)reg_buf;
0028     u8 *val = val_buf;
0029     int i, ret = 0;
0030 
0031     mdev = dev->priv;
0032 
0033     mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED);
0034     for (i = 0; i < val_len; i++) {
0035         int tmp = reg + i;
0036 
0037         ret = __mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) |
0038                      SMI_KSZ88XX_READ_PHY, tmp);
0039         if (ret < 0)
0040             goto out;
0041 
0042         val[i] = ret;
0043     }
0044     ret = 0;
0045 
0046  out:
0047     mutex_unlock(&mdev->bus->mdio_lock);
0048 
0049     return ret;
0050 }
0051 
0052 static int ksz8863_mdio_write(void *ctx, const void *data, size_t count)
0053 {
0054     struct ksz_device *dev = ctx;
0055     struct mdio_device *mdev;
0056     int i, ret = 0;
0057     u32 reg;
0058     u8 *val;
0059 
0060     mdev = dev->priv;
0061 
0062     val = (u8 *)(data + 4);
0063     reg = *(u32 *)data;
0064 
0065     mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED);
0066     for (i = 0; i < (count - 4); i++) {
0067         int tmp = reg + i;
0068 
0069         ret = __mdiobus_write(mdev->bus, ((tmp & 0xE0) >> 5),
0070                       tmp, val[i]);
0071         if (ret < 0)
0072             goto out;
0073     }
0074 
0075  out:
0076     mutex_unlock(&mdev->bus->mdio_lock);
0077 
0078     return ret;
0079 }
0080 
0081 static const struct regmap_bus regmap_smi[] = {
0082     {
0083         .read = ksz8863_mdio_read,
0084         .write = ksz8863_mdio_write,
0085         .max_raw_read = 1,
0086         .max_raw_write = 1,
0087     },
0088     {
0089         .read = ksz8863_mdio_read,
0090         .write = ksz8863_mdio_write,
0091         .val_format_endian_default = REGMAP_ENDIAN_BIG,
0092         .max_raw_read = 2,
0093         .max_raw_write = 2,
0094     },
0095     {
0096         .read = ksz8863_mdio_read,
0097         .write = ksz8863_mdio_write,
0098         .val_format_endian_default = REGMAP_ENDIAN_BIG,
0099         .max_raw_read = 4,
0100         .max_raw_write = 4,
0101     }
0102 };
0103 
0104 static const struct regmap_config ksz8863_regmap_config[] = {
0105     {
0106         .name = "#8",
0107         .reg_bits = 8,
0108         .pad_bits = 24,
0109         .val_bits = 8,
0110         .cache_type = REGCACHE_NONE,
0111         .use_single_read = 1,
0112         .lock = ksz_regmap_lock,
0113         .unlock = ksz_regmap_unlock,
0114     },
0115     {
0116         .name = "#16",
0117         .reg_bits = 8,
0118         .pad_bits = 24,
0119         .val_bits = 16,
0120         .cache_type = REGCACHE_NONE,
0121         .use_single_read = 1,
0122         .lock = ksz_regmap_lock,
0123         .unlock = ksz_regmap_unlock,
0124     },
0125     {
0126         .name = "#32",
0127         .reg_bits = 8,
0128         .pad_bits = 24,
0129         .val_bits = 32,
0130         .cache_type = REGCACHE_NONE,
0131         .use_single_read = 1,
0132         .lock = ksz_regmap_lock,
0133         .unlock = ksz_regmap_unlock,
0134     }
0135 };
0136 
0137 static int ksz8863_smi_probe(struct mdio_device *mdiodev)
0138 {
0139     struct regmap_config rc;
0140     struct ksz_device *dev;
0141     int ret;
0142     int i;
0143 
0144     dev = ksz_switch_alloc(&mdiodev->dev, mdiodev);
0145     if (!dev)
0146         return -ENOMEM;
0147 
0148     for (i = 0; i < ARRAY_SIZE(ksz8863_regmap_config); i++) {
0149         rc = ksz8863_regmap_config[i];
0150         rc.lock_arg = &dev->regmap_mutex;
0151         dev->regmap[i] = devm_regmap_init(&mdiodev->dev,
0152                           &regmap_smi[i], dev,
0153                           &rc);
0154         if (IS_ERR(dev->regmap[i])) {
0155             ret = PTR_ERR(dev->regmap[i]);
0156             dev_err(&mdiodev->dev,
0157                 "Failed to initialize regmap%i: %d\n",
0158                 ksz8863_regmap_config[i].val_bits, ret);
0159             return ret;
0160         }
0161     }
0162 
0163     if (mdiodev->dev.platform_data)
0164         dev->pdata = mdiodev->dev.platform_data;
0165 
0166     ret = ksz_switch_register(dev);
0167 
0168     /* Main DSA driver may not be started yet. */
0169     if (ret)
0170         return ret;
0171 
0172     dev_set_drvdata(&mdiodev->dev, dev);
0173 
0174     return 0;
0175 }
0176 
0177 static void ksz8863_smi_remove(struct mdio_device *mdiodev)
0178 {
0179     struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev);
0180 
0181     if (dev)
0182         ksz_switch_remove(dev);
0183 
0184     dev_set_drvdata(&mdiodev->dev, NULL);
0185 }
0186 
0187 static void ksz8863_smi_shutdown(struct mdio_device *mdiodev)
0188 {
0189     struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev);
0190 
0191     if (dev)
0192         dsa_switch_shutdown(dev->ds);
0193 
0194     dev_set_drvdata(&mdiodev->dev, NULL);
0195 }
0196 
0197 static const struct of_device_id ksz8863_dt_ids[] = {
0198     {
0199         .compatible = "microchip,ksz8863",
0200         .data = &ksz_switch_chips[KSZ8830]
0201     },
0202     {
0203         .compatible = "microchip,ksz8873",
0204         .data = &ksz_switch_chips[KSZ8830]
0205     },
0206     { },
0207 };
0208 MODULE_DEVICE_TABLE(of, ksz8863_dt_ids);
0209 
0210 static struct mdio_driver ksz8863_driver = {
0211     .probe  = ksz8863_smi_probe,
0212     .remove = ksz8863_smi_remove,
0213     .shutdown = ksz8863_smi_shutdown,
0214     .mdiodrv.driver = {
0215         .name   = "ksz8863-switch",
0216         .of_match_table = ksz8863_dt_ids,
0217     },
0218 };
0219 
0220 mdio_module_driver(ksz8863_driver);
0221 
0222 MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>");
0223 MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver");
0224 MODULE_LICENSE("GPL v2");