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