Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

misc: keba: Add LAN9252 driver

KEBA CP500 devices use the LAN9252 controller for EtherCAT
communication. For a stable Ethernet link the PHY registers of the
controller need to be configured correctly. This driver configures these
PHY registers as required.

Signed-off-by: Gerhard Engleder <eg@keba.com>
Link: https://lore.kernel.org/r/20241011191257.19702-5-gerhard@engleder-embedded.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Gerhard Engleder and committed by
Greg Kroah-Hartman
366898e7 79484830

+371
+11
drivers/misc/keba/Kconfig
··· 11 11 12 12 This driver can also be built as a module. If so, the module will be 13 13 called cp500. 14 + 15 + config KEBA_LAN9252 16 + tristate "KEBA CP500 LAN9252 configuration" 17 + depends on SPI 18 + depends on KEBA_CP500 || COMPILE_TEST 19 + help 20 + This driver is used for updating the configuration of the LAN9252 21 + controller on KEBA CP500 devices. 22 + 23 + This driver can also be built as a module. If so, the module will be 24 + called lan9252.
+1
drivers/misc/keba/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 obj-$(CONFIG_KEBA_CP500) += cp500.o 4 + obj-$(CONFIG_KEBA_LAN9252) += lan9252.o
+359
drivers/misc/keba/lan9252.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) KEBA Industrial Automation Gmbh 2024 4 + * 5 + * Driver for LAN9252 on KEBA CP500 devices 6 + * 7 + * This driver is used for updating the configuration of the LAN9252 controller 8 + * on KEBA CP500 devices. The LAN9252 is connected over SPI, which is also named 9 + * PDI. 10 + */ 11 + 12 + #include <linux/spi/spi.h> 13 + #include <linux/mii.h> 14 + 15 + /* SPI commands */ 16 + #define LAN9252_SPI_READ 0x3 17 + #define LAN9252_SPI_WRITE 0x2 18 + 19 + struct lan9252_read_cmd { 20 + u8 cmd; 21 + u8 addr_0; 22 + u8 addr_1; 23 + } __packed; 24 + 25 + struct lan9252_write_cmd { 26 + u8 cmd; 27 + u8 addr_0; 28 + u8 addr_1; 29 + u32 data; 30 + } __packed; 31 + 32 + /* byte test register */ 33 + #define LAN9252_BYTE_TEST 0x64 34 + #define LAN9252_BYTE_TEST_VALUE 0x87654321 35 + 36 + /* hardware configuration register */ 37 + #define LAN9252_HW_CFG 0x74 38 + #define LAN9252_HW_CFG_READY 0x08000000 39 + 40 + /* EtherCAT CSR interface data register */ 41 + #define LAN9252_ECAT_CSR_DATA 0x300 42 + 43 + /* EtherCAT CSR interface command register */ 44 + #define LAN9252_ECAT_CSR_CMD 0x304 45 + #define LAN9252_ECAT_CSR_BUSY 0x80000000 46 + #define LAN9252_ECAT_CSR_READ 0x40000000 47 + 48 + /* EtherCAT slave controller MII register */ 49 + #define LAN9252_ESC_MII 0x510 50 + #define LAN9252_ESC_MII_BUSY 0x8000 51 + #define LAN9252_ESC_MII_CMD_ERR 0x4000 52 + #define LAN9252_ESC_MII_READ_ERR 0x2000 53 + #define LAN9252_ESC_MII_ERR_MASK (LAN9252_ESC_MII_CMD_ERR | \ 54 + LAN9252_ESC_MII_READ_ERR) 55 + #define LAN9252_ESC_MII_WRITE 0x0200 56 + #define LAN9252_ESC_MII_READ 0x0100 57 + 58 + /* EtherCAT slave controller PHY address register */ 59 + #define LAN9252_ESC_PHY_ADDR 0x512 60 + 61 + /* EtherCAT slave controller PHY register address register */ 62 + #define LAN9252_ESC_PHY_REG_ADDR 0x513 63 + 64 + /* EtherCAT slave controller PHY data register */ 65 + #define LAN9252_ESC_PHY_DATA 0x514 66 + 67 + /* EtherCAT slave controller PDI access state register */ 68 + #define LAN9252_ESC_MII_PDI 0x517 69 + #define LAN9252_ESC_MII_ACCESS_PDI 0x01 70 + #define LAN9252_ESC_MII_ACCESS_ECAT 0x00 71 + 72 + /* PHY address */ 73 + #define PHY_ADDRESS 2 74 + 75 + #define SPI_RETRY_COUNT 10 76 + #define SPI_WAIT_US 100 77 + #define SPI_CSR_WAIT_US 500 78 + 79 + static int lan9252_spi_read(struct spi_device *spi, u16 addr, u32 *data) 80 + { 81 + struct lan9252_read_cmd cmd; 82 + 83 + cmd.cmd = LAN9252_SPI_READ; 84 + cmd.addr_0 = (addr >> 8) & 0xFF; 85 + cmd.addr_1 = addr & 0xFF; 86 + 87 + return spi_write_then_read(spi, (u8 *)&cmd, 88 + sizeof(struct lan9252_read_cmd), 89 + (u8 *)data, sizeof(u32)); 90 + } 91 + 92 + static int lan9252_spi_write(struct spi_device *spi, u16 addr, u32 data) 93 + { 94 + struct lan9252_write_cmd cmd; 95 + 96 + cmd.cmd = LAN9252_SPI_WRITE; 97 + cmd.addr_0 = (addr >> 8) & 0xFF; 98 + cmd.addr_1 = addr & 0xFF; 99 + cmd.data = data; 100 + 101 + return spi_write(spi, (u8 *)&cmd, sizeof(struct lan9252_write_cmd)); 102 + } 103 + 104 + static bool lan9252_init(struct spi_device *spi) 105 + { 106 + u32 data; 107 + int ret; 108 + 109 + ret = lan9252_spi_read(spi, LAN9252_BYTE_TEST, &data); 110 + if (ret || data != LAN9252_BYTE_TEST_VALUE) 111 + return false; 112 + 113 + ret = lan9252_spi_read(spi, LAN9252_HW_CFG, &data); 114 + if (ret || !(data & LAN9252_HW_CFG_READY)) 115 + return false; 116 + 117 + return true; 118 + } 119 + 120 + static u8 lan9252_esc_get_size(u16 addr) 121 + { 122 + if (addr == LAN9252_ESC_MII || addr == LAN9252_ESC_PHY_DATA) 123 + return 2; 124 + 125 + return 1; 126 + } 127 + 128 + static int lan9252_esc_wait(struct spi_device *spi) 129 + { 130 + ktime_t timeout = ktime_add_us(ktime_get(), SPI_WAIT_US); 131 + u32 data; 132 + int ret; 133 + 134 + /* wait while CSR command is busy */ 135 + for (;;) { 136 + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); 137 + if (ret) 138 + return ret; 139 + if (!(data & LAN9252_ECAT_CSR_BUSY)) 140 + return 0; 141 + 142 + if (ktime_compare(ktime_get(), timeout) > 0) { 143 + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); 144 + if (ret) 145 + return ret; 146 + break; 147 + } 148 + } 149 + 150 + return (!(data & LAN9252_ECAT_CSR_BUSY)) ? 0 : -ETIMEDOUT; 151 + } 152 + 153 + static int lan9252_esc_read(struct spi_device *spi, u16 addr, u32 *data) 154 + { 155 + u32 csr_cmd; 156 + u8 size; 157 + int ret; 158 + 159 + size = lan9252_esc_get_size(addr); 160 + csr_cmd = LAN9252_ECAT_CSR_BUSY | LAN9252_ECAT_CSR_READ; 161 + csr_cmd |= (size << 16) | addr; 162 + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); 163 + if (ret) 164 + return ret; 165 + 166 + ret = lan9252_esc_wait(spi); 167 + if (ret) 168 + return ret; 169 + 170 + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_DATA, data); 171 + if (ret) 172 + return ret; 173 + 174 + return 0; 175 + } 176 + 177 + static int lan9252_esc_write(struct spi_device *spi, u16 addr, u32 data) 178 + { 179 + u32 csr_cmd; 180 + u8 size; 181 + int ret; 182 + 183 + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_DATA, data); 184 + if (ret) 185 + return ret; 186 + 187 + size = lan9252_esc_get_size(addr); 188 + csr_cmd = LAN9252_ECAT_CSR_BUSY; 189 + csr_cmd |= (size << 16) | addr; 190 + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); 191 + if (ret) 192 + return ret; 193 + 194 + ret = lan9252_esc_wait(spi); 195 + if (ret) 196 + return ret; 197 + 198 + return 0; 199 + } 200 + 201 + static int lan9252_access_mii(struct spi_device *spi, bool access) 202 + { 203 + u32 data; 204 + 205 + if (access) 206 + data = LAN9252_ESC_MII_ACCESS_PDI; 207 + else 208 + data = LAN9252_ESC_MII_ACCESS_ECAT; 209 + 210 + return lan9252_esc_write(spi, LAN9252_ESC_MII_PDI, data); 211 + } 212 + 213 + static int lan9252_mii_wait(struct spi_device *spi) 214 + { 215 + ktime_t timeout = ktime_add_us(ktime_get(), SPI_CSR_WAIT_US); 216 + u32 data; 217 + int ret; 218 + 219 + /* wait while MII control state machine is busy */ 220 + for (;;) { 221 + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); 222 + if (ret) 223 + return ret; 224 + if (data & LAN9252_ESC_MII_ERR_MASK) 225 + return -EIO; 226 + if (!(data & LAN9252_ESC_MII_BUSY)) 227 + return 0; 228 + 229 + if (ktime_compare(ktime_get(), timeout) > 0) { 230 + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); 231 + if (ret) 232 + return ret; 233 + if (data & LAN9252_ESC_MII_ERR_MASK) 234 + return -EIO; 235 + break; 236 + } 237 + } 238 + 239 + return (!(data & LAN9252_ESC_MII_BUSY)) ? 0 : -ETIMEDOUT; 240 + } 241 + 242 + static int lan9252_mii_read(struct spi_device *spi, u8 phy_addr, u8 reg_addr, 243 + u32 *data) 244 + { 245 + int ret; 246 + 247 + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); 248 + if (ret) 249 + return ret; 250 + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); 251 + if (ret) 252 + return ret; 253 + 254 + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_READ); 255 + if (ret) 256 + return ret; 257 + 258 + ret = lan9252_mii_wait(spi); 259 + if (ret) 260 + return ret; 261 + 262 + return lan9252_esc_read(spi, LAN9252_ESC_PHY_DATA, data); 263 + } 264 + 265 + static int lan9252_mii_write(struct spi_device *spi, u8 phy_addr, u8 reg_addr, 266 + u32 data) 267 + { 268 + int ret; 269 + 270 + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); 271 + if (ret) 272 + return ret; 273 + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); 274 + if (ret) 275 + return ret; 276 + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_DATA, data); 277 + if (ret) 278 + return ret; 279 + 280 + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_WRITE); 281 + if (ret) 282 + return ret; 283 + 284 + return lan9252_mii_wait(spi); 285 + } 286 + 287 + static int lan9252_probe(struct spi_device *spi) 288 + { 289 + u32 data; 290 + int retry = SPI_RETRY_COUNT; 291 + int ret; 292 + 293 + /* execute specified initialization sequence */ 294 + while (retry && !lan9252_init(spi)) 295 + retry--; 296 + if (retry == 0) { 297 + dev_err(&spi->dev, 298 + "Can't initialize LAN9252 SPI communication!"); 299 + return -EIO; 300 + } 301 + 302 + /* enable access to MII management for PDI */ 303 + ret = lan9252_access_mii(spi, true); 304 + if (ret) { 305 + dev_err(&spi->dev, "Can't enable access to MII management!"); 306 + return ret; 307 + } 308 + 309 + /* 310 + * check PHY configuration and configure if necessary 311 + * - full duplex 312 + * - auto negotiation disabled 313 + * - 100 Mbps 314 + */ 315 + ret = lan9252_mii_read(spi, PHY_ADDRESS, MII_BMCR, &data); 316 + if (ret) { 317 + dev_err(&spi->dev, "Can't read LAN9252 configuration!"); 318 + goto out; 319 + } 320 + if (!(data & BMCR_FULLDPLX) || (data & BMCR_ANENABLE) || 321 + !(data & BMCR_SPEED100)) { 322 + /* 323 + */ 324 + data &= ~(BMCR_ANENABLE); 325 + data |= (BMCR_FULLDPLX | BMCR_SPEED100); 326 + ret = lan9252_mii_write(spi, PHY_ADDRESS, MII_BMCR, data); 327 + if (ret) 328 + dev_err(&spi->dev, 329 + "Can't write LAN9252 configuration!"); 330 + } 331 + 332 + dev_info(&spi->dev, "LAN9252 PHY configuration"); 333 + 334 + out: 335 + /* disable access to MII management for PDI */ 336 + lan9252_access_mii(spi, false); 337 + 338 + return ret; 339 + } 340 + 341 + static const struct spi_device_id lan9252_id[] = { 342 + {"lan9252"}, 343 + {} 344 + }; 345 + MODULE_DEVICE_TABLE(spi, lan9252_id); 346 + 347 + static struct spi_driver lan9252_driver = { 348 + .driver = { 349 + .name = "lan9252", 350 + }, 351 + .probe = lan9252_probe, 352 + .id_table = lan9252_id, 353 + }; 354 + module_spi_driver(lan9252_driver); 355 + 356 + MODULE_AUTHOR("Petar Bojanic <boja@keba.com>"); 357 + MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); 358 + MODULE_DESCRIPTION("KEBA LAN9252 driver"); 359 + MODULE_LICENSE("GPL");