1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Microchip KSZ8863 series register access through SMI 4 * 5 * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de> 6 */ 7 8 #include "ksz8.h" 9 #include "ksz_common.h" 10 11 /* Serial Management Interface (SMI) uses the following frame format: 12 * 13 * preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle 14 * |frame| OP code |address |address| | | 15 * read | 32x1´s | 01 | 00 | 1xRRR | RRRRR |Z0| 00000000DDDDDDDD | Z 16 * write| 32x1´s | 01 | 00 | 0xRRR | RRRRR |10| xxxxxxxxDDDDDDDD | Z 17 * 18 */ 19 20 #define SMI_KSZ88XX_READ_PHY BIT(4) 21 22 static int ksz8863_mdio_read(void *ctx, const void *reg_buf, size_t reg_len, 23 void *val_buf, size_t val_len) 24 { 25 struct ksz_device *dev = ctx; 26 struct mdio_device *mdev; 27 u8 reg = *(u8 *)reg_buf; 28 u8 *val = val_buf; 29 int i, ret = 0; 30 31 mdev = dev->priv; 32 33 mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); 34 for (i = 0; i < val_len; i++) { 35 int tmp = reg + i; 36 37 ret = __mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) | 38 SMI_KSZ88XX_READ_PHY, tmp); 39 if (ret < 0) 40 goto out; 41 42 val[i] = ret; 43 } 44 ret = 0; 45 46 out: 47 mutex_unlock(&mdev->bus->mdio_lock); 48 49 return ret; 50 } 51 52 static int ksz8863_mdio_write(void *ctx, const void *data, size_t count) 53 { 54 struct ksz_device *dev = ctx; 55 struct mdio_device *mdev; 56 int i, ret = 0; 57 u32 reg; 58 u8 *val; 59 60 mdev = dev->priv; 61 62 val = (u8 *)(data + 4); 63 reg = *(u32 *)data; 64 65 mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); 66 for (i = 0; i < (count - 4); i++) { 67 int tmp = reg + i; 68 69 ret = __mdiobus_write(mdev->bus, ((tmp & 0xE0) >> 5), 70 tmp, val[i]); 71 if (ret < 0) 72 goto out; 73 } 74 75 out: 76 mutex_unlock(&mdev->bus->mdio_lock); 77 78 return ret; 79 } 80 81 static const struct regmap_bus regmap_smi[] = { 82 { 83 .read = ksz8863_mdio_read, 84 .write = ksz8863_mdio_write, 85 }, 86 { 87 .read = ksz8863_mdio_read, 88 .write = ksz8863_mdio_write, 89 .val_format_endian_default = REGMAP_ENDIAN_BIG, 90 }, 91 { 92 .read = ksz8863_mdio_read, 93 .write = ksz8863_mdio_write, 94 .val_format_endian_default = REGMAP_ENDIAN_BIG, 95 } 96 }; 97 98 static const struct regmap_config ksz8863_regmap_config[] = { 99 { 100 .name = "#8", 101 .reg_bits = 8, 102 .pad_bits = 24, 103 .val_bits = 8, 104 .cache_type = REGCACHE_NONE, 105 .lock = ksz_regmap_lock, 106 .unlock = ksz_regmap_unlock, 107 }, 108 { 109 .name = "#16", 110 .reg_bits = 8, 111 .pad_bits = 24, 112 .val_bits = 16, 113 .cache_type = REGCACHE_NONE, 114 .lock = ksz_regmap_lock, 115 .unlock = ksz_regmap_unlock, 116 }, 117 { 118 .name = "#32", 119 .reg_bits = 8, 120 .pad_bits = 24, 121 .val_bits = 32, 122 .cache_type = REGCACHE_NONE, 123 .lock = ksz_regmap_lock, 124 .unlock = ksz_regmap_unlock, 125 } 126 }; 127 128 static int ksz8863_smi_probe(struct mdio_device *mdiodev) 129 { 130 struct regmap_config rc; 131 struct ksz_device *dev; 132 int ret; 133 int i; 134 135 dev = ksz_switch_alloc(&mdiodev->dev, mdiodev); 136 if (!dev) 137 return -ENOMEM; 138 139 for (i = 0; i < ARRAY_SIZE(ksz8863_regmap_config); i++) { 140 rc = ksz8863_regmap_config[i]; 141 rc.lock_arg = &dev->regmap_mutex; 142 dev->regmap[i] = devm_regmap_init(&mdiodev->dev, 143 ®map_smi[i], dev, 144 &rc); 145 if (IS_ERR(dev->regmap[i])) { 146 return dev_err_probe(&mdiodev->dev, 147 PTR_ERR(dev->regmap[i]), 148 "Failed to initialize regmap%i\n", 149 ksz8863_regmap_config[i].val_bits); 150 } 151 } 152 153 if (mdiodev->dev.platform_data) 154 dev->pdata = mdiodev->dev.platform_data; 155 156 ret = ksz_switch_register(dev); 157 158 /* Main DSA driver may not be started yet. */ 159 if (ret) 160 return ret; 161 162 dev_set_drvdata(&mdiodev->dev, dev); 163 164 return 0; 165 } 166 167 static void ksz8863_smi_remove(struct mdio_device *mdiodev) 168 { 169 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 170 171 if (dev) 172 ksz_switch_remove(dev); 173 } 174 175 static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) 176 { 177 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 178 179 if (dev) 180 dsa_switch_shutdown(dev->ds); 181 182 dev_set_drvdata(&mdiodev->dev, NULL); 183 } 184 185 static const struct of_device_id ksz8863_dt_ids[] = { 186 { 187 .compatible = "microchip,ksz8863", 188 .data = &ksz_switch_chips[KSZ8830] 189 }, 190 { 191 .compatible = "microchip,ksz8873", 192 .data = &ksz_switch_chips[KSZ8830] 193 }, 194 { }, 195 }; 196 MODULE_DEVICE_TABLE(of, ksz8863_dt_ids); 197 198 static struct mdio_driver ksz8863_driver = { 199 .probe = ksz8863_smi_probe, 200 .remove = ksz8863_smi_remove, 201 .shutdown = ksz8863_smi_shutdown, 202 .mdiodrv.driver = { 203 .name = "ksz8863-switch", 204 .of_match_table = ksz8863_dt_ids, 205 }, 206 }; 207 208 mdio_module_driver(ksz8863_driver); 209 210 MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>"); 211 MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver"); 212 MODULE_LICENSE("GPL v2"); 213