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 .max_raw_read = 1, 86 .max_raw_write = 1, 87 }, 88 { 89 .read = ksz8863_mdio_read, 90 .write = ksz8863_mdio_write, 91 .val_format_endian_default = REGMAP_ENDIAN_BIG, 92 .max_raw_read = 2, 93 .max_raw_write = 2, 94 }, 95 { 96 .read = ksz8863_mdio_read, 97 .write = ksz8863_mdio_write, 98 .val_format_endian_default = REGMAP_ENDIAN_BIG, 99 .max_raw_read = 4, 100 .max_raw_write = 4, 101 } 102 }; 103 104 static const struct regmap_config ksz8863_regmap_config[] = { 105 { 106 .name = "#8", 107 .reg_bits = 8, 108 .pad_bits = 24, 109 .val_bits = 8, 110 .cache_type = REGCACHE_NONE, 111 .use_single_read = 1, 112 .lock = ksz_regmap_lock, 113 .unlock = ksz_regmap_unlock, 114 }, 115 { 116 .name = "#16", 117 .reg_bits = 8, 118 .pad_bits = 24, 119 .val_bits = 16, 120 .cache_type = REGCACHE_NONE, 121 .use_single_read = 1, 122 .lock = ksz_regmap_lock, 123 .unlock = ksz_regmap_unlock, 124 }, 125 { 126 .name = "#32", 127 .reg_bits = 8, 128 .pad_bits = 24, 129 .val_bits = 32, 130 .cache_type = REGCACHE_NONE, 131 .use_single_read = 1, 132 .lock = ksz_regmap_lock, 133 .unlock = ksz_regmap_unlock, 134 } 135 }; 136 137 static int ksz8863_smi_probe(struct mdio_device *mdiodev) 138 { 139 struct regmap_config rc; 140 struct ksz_device *dev; 141 int ret; 142 int i; 143 144 dev = ksz_switch_alloc(&mdiodev->dev, mdiodev); 145 if (!dev) 146 return -ENOMEM; 147 148 for (i = 0; i < ARRAY_SIZE(ksz8863_regmap_config); i++) { 149 rc = ksz8863_regmap_config[i]; 150 rc.lock_arg = &dev->regmap_mutex; 151 dev->regmap[i] = devm_regmap_init(&mdiodev->dev, 152 ®map_smi[i], dev, 153 &rc); 154 if (IS_ERR(dev->regmap[i])) { 155 ret = PTR_ERR(dev->regmap[i]); 156 dev_err(&mdiodev->dev, 157 "Failed to initialize regmap%i: %d\n", 158 ksz8863_regmap_config[i].val_bits, ret); 159 return ret; 160 } 161 } 162 163 if (mdiodev->dev.platform_data) 164 dev->pdata = mdiodev->dev.platform_data; 165 166 ret = ksz_switch_register(dev); 167 168 /* Main DSA driver may not be started yet. */ 169 if (ret) 170 return ret; 171 172 dev_set_drvdata(&mdiodev->dev, dev); 173 174 return 0; 175 } 176 177 static void ksz8863_smi_remove(struct mdio_device *mdiodev) 178 { 179 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 180 181 if (dev) 182 ksz_switch_remove(dev); 183 184 dev_set_drvdata(&mdiodev->dev, NULL); 185 } 186 187 static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) 188 { 189 struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); 190 191 if (dev) 192 dsa_switch_shutdown(dev->ds); 193 194 dev_set_drvdata(&mdiodev->dev, NULL); 195 } 196 197 static const struct of_device_id ksz8863_dt_ids[] = { 198 { 199 .compatible = "microchip,ksz8863", 200 .data = &ksz_switch_chips[KSZ8830] 201 }, 202 { 203 .compatible = "microchip,ksz8873", 204 .data = &ksz_switch_chips[KSZ8830] 205 }, 206 { }, 207 }; 208 MODULE_DEVICE_TABLE(of, ksz8863_dt_ids); 209 210 static struct mdio_driver ksz8863_driver = { 211 .probe = ksz8863_smi_probe, 212 .remove = ksz8863_smi_remove, 213 .shutdown = ksz8863_smi_shutdown, 214 .mdiodrv.driver = { 215 .name = "ksz8863-switch", 216 .of_match_table = ksz8863_dt_ids, 217 }, 218 }; 219 220 mdio_module_driver(ksz8863_driver); 221 222 MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>"); 223 MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver"); 224 MODULE_LICENSE("GPL v2"); 225