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