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 struct ksz8 *ksz8; 30 int i, ret = 0; 31 32 ksz8 = dev->priv; 33 mdev = ksz8->priv; 34 35 mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); 36 for (i = 0; i < val_len; i++) { 37 int tmp = reg + i; 38 39 ret = __mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) | 40 SMI_KSZ88XX_READ_PHY, tmp); 41 if (ret < 0) 42 goto out; 43 44 val[i] = ret; 45 } 46 ret = 0; 47 48 out: 49 mutex_unlock(&mdev->bus->mdio_lock); 50 51 return ret; 52 } 53 54 static int ksz8863_mdio_write(void *ctx, const void *data, size_t count) 55 { 56 struct ksz_device *dev = ctx; 57 struct mdio_device *mdev; 58 struct ksz8 *ksz8; 59 int i, ret = 0; 60 u32 reg; 61 u8 *val; 62 63 ksz8 = dev->priv; 64 mdev = ksz8->priv; 65 66 val = (u8 *)(data + 4); 67 reg = *(u32 *)data; 68 69 mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); 70 for (i = 0; i < (count - 4); i++) { 71 int tmp = reg + i; 72 73 ret = __mdiobus_write(mdev->bus, ((tmp & 0xE0) >> 5), 74 tmp, val[i]); 75 if (ret < 0) 76 goto out; 77 } 78 79 out: 80 mutex_unlock(&mdev->bus->mdio_lock); 81 82 return ret; 83 } 84 85 static const struct regmap_bus regmap_smi[] = { 86 { 87 .read = ksz8863_mdio_read, 88 .write = ksz8863_mdio_write, 89 .max_raw_read = 1, 90 .max_raw_write = 1, 91 }, 92 { 93 .read = ksz8863_mdio_read, 94 .write = ksz8863_mdio_write, 95 .val_format_endian_default = REGMAP_ENDIAN_BIG, 96 .max_raw_read = 2, 97 .max_raw_write = 2, 98 }, 99 { 100 .read = ksz8863_mdio_read, 101 .write = ksz8863_mdio_write, 102 .val_format_endian_default = REGMAP_ENDIAN_BIG, 103 .max_raw_read = 4, 104 .max_raw_write = 4, 105 } 106 }; 107 108 static const struct regmap_config ksz8863_regmap_config[] = { 109 { 110 .name = "#8", 111 .reg_bits = 8, 112 .pad_bits = 24, 113 .val_bits = 8, 114 .cache_type = REGCACHE_NONE, 115 .use_single_read = 1, 116 .lock = ksz_regmap_lock, 117 .unlock = ksz_regmap_unlock, 118 }, 119 { 120 .name = "#16", 121 .reg_bits = 8, 122 .pad_bits = 24, 123 .val_bits = 16, 124 .cache_type = REGCACHE_NONE, 125 .use_single_read = 1, 126 .lock = ksz_regmap_lock, 127 .unlock = ksz_regmap_unlock, 128 }, 129 { 130 .name = "#32", 131 .reg_bits = 8, 132 .pad_bits = 24, 133 .val_bits = 32, 134 .cache_type = REGCACHE_NONE, 135 .use_single_read = 1, 136 .lock = ksz_regmap_lock, 137 .unlock = ksz_regmap_unlock, 138 } 139 }; 140 141 static int ksz8863_smi_probe(struct mdio_device *mdiodev) 142 { 143 struct regmap_config rc; 144 struct ksz_device *dev; 145 struct ksz8 *ksz8; 146 int ret; 147 int i; 148 149 ksz8 = devm_kzalloc(&mdiodev->dev, sizeof(struct ksz8), GFP_KERNEL); 150 if (!ksz8) 151 return -ENOMEM; 152 153 ksz8->priv = mdiodev; 154 155 dev = ksz_switch_alloc(&mdiodev->dev, ksz8); 156 if (!dev) 157 return -ENOMEM; 158 159 for (i = 0; i < ARRAY_SIZE(ksz8863_regmap_config); i++) { 160 rc = ksz8863_regmap_config[i]; 161 rc.lock_arg = &dev->regmap_mutex; 162 dev->regmap[i] = devm_regmap_init(&mdiodev->dev, 163 ®map_smi[i], dev, 164 &rc); 165 if (IS_ERR(dev->regmap[i])) { 166 ret = PTR_ERR(dev->regmap[i]); 167 dev_err(&mdiodev->dev, 168 "Failed to initialize regmap%i: %d\n", 169 ksz8863_regmap_config[i].val_bits, ret); 170 return ret; 171 } 172 } 173 174 if (mdiodev->dev.platform_data) 175 dev->pdata = mdiodev->dev.platform_data; 176 177 ret = ksz8_switch_register(dev); 178 179 /* Main DSA driver may not be started yet. */ 180 if (ret) 181 return ret; 182 183 dev_set_drvdata(&mdiodev->dev, dev); 184 185 return 0; 186 } 187 188 static void ksz8863_smi_remove(struct mdio_device *mdiodev) 189 { 190 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 191 192 if (dev) 193 ksz_switch_remove(dev); 194 195 dev_set_drvdata(&mdiodev->dev, NULL); 196 } 197 198 static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) 199 { 200 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 201 202 if (dev) 203 dsa_switch_shutdown(dev->ds); 204 205 dev_set_drvdata(&mdiodev->dev, NULL); 206 } 207 208 static const struct of_device_id ksz8863_dt_ids[] = { 209 { .compatible = "microchip,ksz8863" }, 210 { .compatible = "microchip,ksz8873" }, 211 { }, 212 }; 213 MODULE_DEVICE_TABLE(of, ksz8863_dt_ids); 214 215 static struct mdio_driver ksz8863_driver = { 216 .probe = ksz8863_smi_probe, 217 .remove = ksz8863_smi_remove, 218 .shutdown = ksz8863_smi_shutdown, 219 .mdiodrv.driver = { 220 .name = "ksz8863-switch", 221 .of_match_table = ksz8863_dt_ids, 222 }, 223 }; 224 225 mdio_module_driver(ksz8863_driver); 226 227 MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>"); 228 MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver"); 229 MODULE_LICENSE("GPL v2"); 230