1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // silicom-platform.c - Silicom MEC170x platform driver 4 // 5 // Copyright (C) 2023 Henry Shi <henrys@silicom-usa.com> 6 #include <linux/bitfield.h> 7 #include <linux/bits.h> 8 #include <linux/dmi.h> 9 #include <linux/hwmon.h> 10 #include <linux/init.h> 11 #include <linux/ioport.h> 12 #include <linux/io.h> 13 #include <linux/kernel.h> 14 #include <linux/kobject.h> 15 #include <linux/led-class-multicolor.h> 16 #include <linux/module.h> 17 #include <linux/mutex.h> 18 #include <linux/platform_device.h> 19 #include <linux/string.h> 20 #include <linux/sysfs.h> 21 #include <linux/units.h> 22 23 #include <linux/gpio/driver.h> 24 25 #define MEC_POWER_CYCLE_ADDR 0x24 26 #define MEC_EFUSE_LSB_ADDR 0x28 27 #define MEC_GPIO_IN_POS 0x08 28 #define MEC_IO_BASE 0x0800 29 #define MEC_IO_LEN 0x8 30 #define IO_REG_BANK 0x0 31 #define DEFAULT_CHAN_LO 0 32 #define DEFAULT_CHAN_HI 0 33 #define DEFAULT_CHAN_LO_T 0xc 34 #define MEC_ADDR (MEC_IO_BASE + 0x02) 35 #define EC_ADDR_LSB MEC_ADDR 36 #define SILICOM_MEC_MAGIC 0x5a 37 38 #define MEC_PORT_CHANNEL_MASK GENMASK(2, 0) 39 #define MEC_PORT_DWORD_OFFSET GENMASK(31, 3) 40 #define MEC_DATA_OFFSET_MASK GENMASK(1, 0) 41 #define MEC_PORT_OFFSET_MASK GENMASK(7, 2) 42 43 #define MEC_TEMP_LOC GENMASK(31, 16) 44 #define MEC_VERSION_LOC GENMASK(15, 8) 45 #define MEC_VERSION_MAJOR GENMASK(15, 14) 46 #define MEC_VERSION_MINOR GENMASK(13, 8) 47 48 #define EC_ADDR_MSB (MEC_IO_BASE + 0x3) 49 #define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset)) 50 51 #define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit)) 52 #define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14) 53 54 static DEFINE_MUTEX(mec_io_mutex); 55 static unsigned int efuse_status; 56 static unsigned int mec_uc_version; 57 static unsigned int power_cycle; 58 59 static const struct hwmon_channel_info *silicom_fan_control_info[] = { 60 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL), 61 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), 62 NULL 63 }; 64 65 struct silicom_platform_info { 66 int io_base; 67 int io_len; 68 struct led_classdev_mc *led_info; 69 struct gpio_chip *gpiochip; 70 u8 *gpio_channels; 71 u16 ngpio; 72 }; 73 74 static const char * const plat_0222_gpio_names[] = { 75 "AUTOM0_SFP_TX_FAULT", 76 "SLOT2_LED_OUT", 77 "SIM_M2_SLOT2_B_DET", 78 "SIM_M2_SLOT2_A_DET", 79 "SLOT1_LED_OUT", 80 "SIM_M2_SLOT1_B_DET", 81 "SIM_M2_SLOT1_A_DET", 82 "SLOT0_LED_OUT", 83 "WAN_SFP0_RX_LOS", 84 "WAN_SFP0_PRSNT_N", 85 "WAN_SFP0_TX_FAULT", 86 "AUTOM1_SFP_RX_LOS", 87 "AUTOM1_SFP_PRSNT_N", 88 "AUTOM1_SFP_TX_FAULT", 89 "AUTOM0_SFP_RX_LOS", 90 "AUTOM0_SFP_PRSNT_N", 91 "WAN_SFP1_RX_LOS", 92 "WAN_SFP1_PRSNT_N", 93 "WAN_SFP1_TX_FAULT", 94 "SIM_M2_SLOT1_MUX_SEL", 95 "W_DISABLE_M2_SLOT1_N", 96 "W_DISABLE_MPCIE_SLOT0_N", 97 "W_DISABLE_M2_SLOT0_N", 98 "BT_COMMAND_MODE", 99 "WAN_SFP1_TX_DISABLE", 100 "WAN_SFP0_TX_DISABLE", 101 "AUTOM1_SFP_TX_DISABLE", 102 "AUTOM0_SFP_TX_DISABLE", 103 "SIM_M2_SLOT2_MUX_SEL", 104 "W_DISABLE_M2_SLOT2_N", 105 "RST_CTL_M2_SLOT_1_N", 106 "RST_CTL_M2_SLOT_2_N", 107 "PM_USB_PWR_EN_BOT", 108 "PM_USB_PWR_EN_TOP", 109 }; 110 111 static u8 plat_0222_gpio_channels[] = { 112 OFFSET_BIT_TO_CHANNEL(0x00, 0), 113 OFFSET_BIT_TO_CHANNEL(0x00, 1), 114 OFFSET_BIT_TO_CHANNEL(0x00, 2), 115 OFFSET_BIT_TO_CHANNEL(0x00, 3), 116 OFFSET_BIT_TO_CHANNEL(0x00, 4), 117 OFFSET_BIT_TO_CHANNEL(0x00, 5), 118 OFFSET_BIT_TO_CHANNEL(0x00, 6), 119 OFFSET_BIT_TO_CHANNEL(0x00, 7), 120 OFFSET_BIT_TO_CHANNEL(0x01, 0), 121 OFFSET_BIT_TO_CHANNEL(0x01, 1), 122 OFFSET_BIT_TO_CHANNEL(0x01, 2), 123 OFFSET_BIT_TO_CHANNEL(0x01, 3), 124 OFFSET_BIT_TO_CHANNEL(0x01, 4), 125 OFFSET_BIT_TO_CHANNEL(0x01, 5), 126 OFFSET_BIT_TO_CHANNEL(0x01, 6), 127 OFFSET_BIT_TO_CHANNEL(0x01, 7), 128 OFFSET_BIT_TO_CHANNEL(0x02, 0), 129 OFFSET_BIT_TO_CHANNEL(0x02, 1), 130 OFFSET_BIT_TO_CHANNEL(0x02, 2), 131 OFFSET_BIT_TO_CHANNEL(0x09, 0), 132 OFFSET_BIT_TO_CHANNEL(0x09, 1), 133 OFFSET_BIT_TO_CHANNEL(0x09, 2), 134 OFFSET_BIT_TO_CHANNEL(0x09, 3), 135 OFFSET_BIT_TO_CHANNEL(0x0a, 0), 136 OFFSET_BIT_TO_CHANNEL(0x0a, 1), 137 OFFSET_BIT_TO_CHANNEL(0x0a, 2), 138 OFFSET_BIT_TO_CHANNEL(0x0a, 3), 139 OFFSET_BIT_TO_CHANNEL(0x0a, 4), 140 OFFSET_BIT_TO_CHANNEL(0x0a, 5), 141 OFFSET_BIT_TO_CHANNEL(0x0a, 6), 142 OFFSET_BIT_TO_CHANNEL(0x0b, 0), 143 OFFSET_BIT_TO_CHANNEL(0x0b, 1), 144 OFFSET_BIT_TO_CHANNEL(0x0b, 2), 145 OFFSET_BIT_TO_CHANNEL(0x0b, 3), 146 }; 147 148 static struct platform_device *silicom_platform_dev; 149 static struct led_classdev_mc *silicom_led_info __initdata; 150 static struct gpio_chip *silicom_gpiochip __initdata; 151 static u8 *silicom_gpio_channels __initdata; 152 153 static int silicom_mec_port_get(unsigned int offset) 154 { 155 unsigned short mec_data_addr; 156 unsigned short mec_port_addr; 157 u8 reg; 158 159 mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK; 160 mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK; 161 162 mutex_lock(&mec_io_mutex); 163 outb(mec_port_addr, MEC_ADDR); 164 reg = inb(MEC_DATA_OFFSET(mec_data_addr)); 165 mutex_unlock(&mec_io_mutex); 166 167 return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01; 168 } 169 170 static enum led_brightness silicom_mec_led_get(int channel) 171 { 172 /* Outputs are active low */ 173 return silicom_mec_port_get(channel) ? LED_OFF : LED_ON; 174 } 175 176 static void silicom_mec_port_set(int channel, int on) 177 { 178 179 unsigned short mec_data_addr; 180 unsigned short mec_port_addr; 181 u8 reg; 182 183 mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK; 184 mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK; 185 186 mutex_lock(&mec_io_mutex); 187 outb(mec_port_addr, MEC_ADDR); 188 reg = inb(MEC_DATA_OFFSET(mec_data_addr)); 189 /* Outputs are active low, so clear the bit for on, or set it for off */ 190 if (on) 191 reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK)); 192 else 193 reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK); 194 outb(reg, MEC_DATA_OFFSET(mec_data_addr)); 195 mutex_unlock(&mec_io_mutex); 196 } 197 198 static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev) 199 { 200 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev); 201 enum led_brightness brightness = LED_OFF; 202 int i; 203 204 for (i = 0; i < mc_cdev->num_colors; i++) { 205 mc_cdev->subled_info[i].brightness = 206 silicom_mec_led_get(mc_cdev->subled_info[i].channel); 207 /* Mark the overall brightness as LED_ON if any of the subleds are on */ 208 if (mc_cdev->subled_info[i].brightness != LED_OFF) 209 brightness = LED_ON; 210 } 211 212 return brightness; 213 } 214 215 static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev, 216 enum led_brightness brightness) 217 { 218 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev); 219 int i; 220 221 led_mc_calc_color_components(mc_cdev, brightness); 222 for (i = 0; i < mc_cdev->num_colors; i++) { 223 silicom_mec_port_set(mc_cdev->subled_info[i].channel, 224 mc_cdev->subled_info[i].brightness); 225 } 226 } 227 228 static int silicom_gpio_get_direction(struct gpio_chip *gc, 229 unsigned int offset) 230 { 231 u8 *channels = gpiochip_get_data(gc); 232 233 /* Input registers have offsets between [0x00, 0x07] */ 234 if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS) 235 return GPIO_LINE_DIRECTION_IN; 236 237 return GPIO_LINE_DIRECTION_OUT; 238 } 239 240 static int silicom_gpio_direction_input(struct gpio_chip *gc, 241 unsigned int offset) 242 { 243 int direction = silicom_gpio_get_direction(gc, offset); 244 245 return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL; 246 } 247 248 static void silicom_gpio_set(struct gpio_chip *gc, 249 unsigned int offset, 250 int value) 251 { 252 int direction = silicom_gpio_get_direction(gc, offset); 253 u8 *channels = gpiochip_get_data(gc); 254 int channel = channels[offset]; 255 256 if (direction == GPIO_LINE_DIRECTION_IN) 257 return; 258 259 silicom_mec_port_set(channel, !value); 260 } 261 262 static int silicom_gpio_direction_output(struct gpio_chip *gc, 263 unsigned int offset, 264 int value) 265 { 266 int direction = silicom_gpio_get_direction(gc, offset); 267 268 if (direction == GPIO_LINE_DIRECTION_IN) 269 return -EINVAL; 270 271 silicom_gpio_set(gc, offset, value); 272 273 return 0; 274 } 275 276 static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset) 277 { 278 u8 *channels = gpiochip_get_data(gc); 279 int channel = channels[offset]; 280 281 return silicom_mec_port_get(channel); 282 } 283 284 static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = { 285 { 286 .color_index = LED_COLOR_ID_WHITE, 287 .brightness = 1, 288 .intensity = 0, 289 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7), 290 }, 291 { 292 .color_index = LED_COLOR_ID_YELLOW, 293 .brightness = 1, 294 .intensity = 0, 295 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6), 296 }, 297 { 298 .color_index = LED_COLOR_ID_RED, 299 .brightness = 1, 300 .intensity = 0, 301 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5), 302 }, 303 }; 304 305 static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = { 306 { 307 .color_index = LED_COLOR_ID_WHITE, 308 .brightness = 1, 309 .intensity = 0, 310 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4), 311 }, 312 { 313 .color_index = LED_COLOR_ID_AMBER, 314 .brightness = 1, 315 .intensity = 0, 316 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3), 317 }, 318 { 319 .color_index = LED_COLOR_ID_RED, 320 .brightness = 1, 321 .intensity = 0, 322 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2), 323 }, 324 }; 325 326 static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = { 327 { 328 .color_index = LED_COLOR_ID_RED, 329 .brightness = 1, 330 .intensity = 0, 331 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1), 332 }, 333 { 334 .color_index = LED_COLOR_ID_GREEN, 335 .brightness = 1, 336 .intensity = 0, 337 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0), 338 }, 339 { 340 .color_index = LED_COLOR_ID_BLUE, 341 .brightness = 1, 342 .intensity = 0, 343 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7), 344 }, 345 { 346 .color_index = LED_COLOR_ID_YELLOW, 347 .brightness = 1, 348 .intensity = 0, 349 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6), 350 }, 351 }; 352 353 static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = { 354 { 355 .color_index = LED_COLOR_ID_RED, 356 .brightness = 1, 357 .intensity = 0, 358 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5), 359 }, 360 { 361 .color_index = LED_COLOR_ID_GREEN, 362 .brightness = 1, 363 .intensity = 0, 364 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4), 365 }, 366 { 367 .color_index = LED_COLOR_ID_BLUE, 368 .brightness = 1, 369 .intensity = 0, 370 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3), 371 }, 372 { 373 .color_index = LED_COLOR_ID_YELLOW, 374 .brightness = 1, 375 .intensity = 0, 376 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2), 377 }, 378 }; 379 380 static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = { 381 { 382 .color_index = LED_COLOR_ID_RED, 383 .brightness = 1, 384 .intensity = 0, 385 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1), 386 }, 387 { 388 .color_index = LED_COLOR_ID_GREEN, 389 .brightness = 1, 390 .intensity = 0, 391 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0), 392 }, 393 { 394 .color_index = LED_COLOR_ID_BLUE, 395 .brightness = 1, 396 .intensity = 0, 397 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1), 398 }, 399 { 400 .color_index = LED_COLOR_ID_YELLOW, 401 .brightness = 1, 402 .intensity = 0, 403 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0), 404 }, 405 }; 406 407 static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = { 408 { 409 .led_cdev = { 410 .name = "platled::wan", 411 .brightness = 0, 412 .max_brightness = 1, 413 .brightness_set = silicom_mec_led_mc_brightness_set, 414 .brightness_get = silicom_mec_led_mc_brightness_get, 415 }, 416 .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info), 417 .subled_info = plat_0222_wan_mc_subled_info, 418 }, 419 { 420 .led_cdev = { 421 .name = "platled::sys", 422 .brightness = 0, 423 .max_brightness = 1, 424 .brightness_set = silicom_mec_led_mc_brightness_set, 425 .brightness_get = silicom_mec_led_mc_brightness_get, 426 }, 427 .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info), 428 .subled_info = plat_0222_sys_mc_subled_info, 429 }, 430 { 431 .led_cdev = { 432 .name = "platled::stat1", 433 .brightness = 0, 434 .max_brightness = 1, 435 .brightness_set = silicom_mec_led_mc_brightness_set, 436 .brightness_get = silicom_mec_led_mc_brightness_get, 437 }, 438 .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info), 439 .subled_info = plat_0222_stat1_mc_subled_info, 440 }, 441 { 442 .led_cdev = { 443 .name = "platled::stat2", 444 .brightness = 0, 445 .max_brightness = 1, 446 .brightness_set = silicom_mec_led_mc_brightness_set, 447 .brightness_get = silicom_mec_led_mc_brightness_get, 448 }, 449 .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info), 450 .subled_info = plat_0222_stat2_mc_subled_info, 451 }, 452 { 453 .led_cdev = { 454 .name = "platled::stat3", 455 .brightness = 0, 456 .max_brightness = 1, 457 .brightness_set = silicom_mec_led_mc_brightness_set, 458 .brightness_get = silicom_mec_led_mc_brightness_get, 459 }, 460 .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info), 461 .subled_info = plat_0222_stat3_mc_subled_info, 462 }, 463 { }, 464 }; 465 466 static struct gpio_chip silicom_gpio_chip = { 467 .label = "silicom-gpio", 468 .get_direction = silicom_gpio_get_direction, 469 .direction_input = silicom_gpio_direction_input, 470 .direction_output = silicom_gpio_direction_output, 471 .get = silicom_gpio_get, 472 .set = silicom_gpio_set, 473 .base = -1, 474 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 475 .names = plat_0222_gpio_names, 476 /* 477 * We're using a mutex to protect the indirect access, so we can sleep 478 * if the lock blocks 479 */ 480 .can_sleep = true, 481 }; 482 483 static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = { 484 .io_base = MEC_IO_BASE, 485 .io_len = MEC_IO_LEN, 486 .led_info = plat_0222_mc_led_info, 487 .gpiochip = &silicom_gpio_chip, 488 .gpio_channels = plat_0222_gpio_channels, 489 /* 490 * The original generic cordoba does not have the last 4 outputs of the 491 * plat_0222 variant, the rest are the same, so use the same longer list, 492 * but ignore the last entries here 493 */ 494 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 495 496 }; 497 498 static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = { 499 { 500 .color_index = LED_COLOR_ID_RED, 501 .brightness = 1, 502 .intensity = 0, 503 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6), 504 }, 505 { 506 .color_index = LED_COLOR_ID_GREEN, 507 .brightness = 1, 508 .intensity = 0, 509 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5), 510 }, 511 { 512 .color_index = LED_COLOR_ID_BLUE, 513 .brightness = 1, 514 .intensity = 0, 515 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7), 516 }, 517 { 518 .color_index = LED_COLOR_ID_AMBER, 519 .brightness = 1, 520 .intensity = 0, 521 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4), 522 }, 523 }; 524 525 static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = { 526 { 527 .color_index = LED_COLOR_ID_RED, 528 .brightness = 1, 529 .intensity = 0, 530 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7), 531 }, 532 { 533 .color_index = LED_COLOR_ID_GREEN, 534 .brightness = 1, 535 .intensity = 0, 536 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4), 537 }, 538 { 539 .color_index = LED_COLOR_ID_BLUE, 540 .brightness = 1, 541 .intensity = 0, 542 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3), 543 }, 544 { 545 .color_index = LED_COLOR_ID_AMBER, 546 .brightness = 1, 547 .intensity = 0, 548 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6), 549 }, 550 }; 551 552 static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = { 553 { 554 .color_index = LED_COLOR_ID_RED, 555 .brightness = 1, 556 .intensity = 0, 557 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2), 558 }, 559 { 560 .color_index = LED_COLOR_ID_GREEN, 561 .brightness = 1, 562 .intensity = 0, 563 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1), 564 }, 565 { 566 .color_index = LED_COLOR_ID_BLUE, 567 .brightness = 1, 568 .intensity = 0, 569 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0), 570 }, 571 { 572 .color_index = LED_COLOR_ID_AMBER, 573 .brightness = 1, 574 .intensity = 0, 575 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5), 576 }, 577 }; 578 579 static struct led_classdev_mc cordoba_mc_led_info[] __initdata = { 580 { 581 .led_cdev = { 582 .name = "platled::fp_left", 583 .brightness = 0, 584 .max_brightness = 1, 585 .brightness_set = silicom_mec_led_mc_brightness_set, 586 .brightness_get = silicom_mec_led_mc_brightness_get, 587 }, 588 .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info), 589 .subled_info = cordoba_fp_left_mc_subled_info, 590 }, 591 { 592 .led_cdev = { 593 .name = "platled::fp_center", 594 .brightness = 0, 595 .max_brightness = 1, 596 .brightness_set = silicom_mec_led_mc_brightness_set, 597 .brightness_get = silicom_mec_led_mc_brightness_get, 598 }, 599 .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info), 600 .subled_info = cordoba_fp_center_mc_subled_info, 601 }, 602 { 603 .led_cdev = { 604 .name = "platled::fp_right", 605 .brightness = 0, 606 .max_brightness = 1, 607 .brightness_set = silicom_mec_led_mc_brightness_set, 608 .brightness_get = silicom_mec_led_mc_brightness_get, 609 }, 610 .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info), 611 .subled_info = cordoba_fp_right_mc_subled_info, 612 }, 613 { }, 614 }; 615 616 static struct silicom_platform_info silicom_generic_cordoba_info __initdata = { 617 .io_base = MEC_IO_BASE, 618 .io_len = MEC_IO_LEN, 619 .led_info = cordoba_mc_led_info, 620 .gpiochip = &silicom_gpio_chip, 621 .gpio_channels = plat_0222_gpio_channels, 622 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 623 }; 624 625 /* 626 * sysfs interface 627 */ 628 static ssize_t efuse_status_show(struct device *dev, 629 struct device_attribute *attr, 630 char *buf) 631 { 632 u32 reg; 633 634 mutex_lock(&mec_io_mutex); 635 /* Select memory region */ 636 outb(IO_REG_BANK, EC_ADDR_MSB); 637 outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB); 638 639 /* Get current data from the address */ 640 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 641 mutex_unlock(&mec_io_mutex); 642 643 efuse_status = reg & 0x1; 644 645 return sysfs_emit(buf, "%u\n", efuse_status); 646 } 647 static DEVICE_ATTR_RO(efuse_status); 648 649 static ssize_t uc_version_show(struct device *dev, 650 struct device_attribute *attr, 651 char *buf) 652 { 653 int uc_version; 654 u32 reg; 655 656 mutex_lock(&mec_io_mutex); 657 outb(IO_REG_BANK, EC_ADDR_MSB); 658 outb(DEFAULT_CHAN_LO, EC_ADDR_LSB); 659 660 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 661 mutex_unlock(&mec_io_mutex); 662 uc_version = FIELD_GET(MEC_VERSION_LOC, reg); 663 if (uc_version >= 192) 664 return -EINVAL; 665 666 uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 + 667 FIELD_GET(MEC_VERSION_MINOR, reg); 668 669 mec_uc_version = uc_version; 670 671 return sysfs_emit(buf, "%u\n", mec_uc_version); 672 } 673 static DEVICE_ATTR_RO(uc_version); 674 675 static ssize_t power_cycle_show(struct device *dev, 676 struct device_attribute *attr, 677 char *buf) 678 { 679 return sysfs_emit(buf, "%u\n", power_cycle); 680 } 681 682 static void powercycle_uc(void) 683 { 684 /* Select memory region */ 685 outb(IO_REG_BANK, EC_ADDR_MSB); 686 outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB); 687 688 /* Set to 1 for current data from the address */ 689 outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 690 } 691 692 static ssize_t power_cycle_store(struct device *dev, 693 struct device_attribute *attr, 694 const char *buf, size_t count) 695 { 696 int rc; 697 unsigned int power_cycle_cmd; 698 699 rc = kstrtou32(buf, 0, &power_cycle_cmd); 700 if (rc) 701 return -EINVAL; 702 703 if (power_cycle_cmd > 0) { 704 mutex_lock(&mec_io_mutex); 705 power_cycle = power_cycle_cmd; 706 powercycle_uc(); 707 mutex_unlock(&mec_io_mutex); 708 } 709 710 return count; 711 } 712 static DEVICE_ATTR_RW(power_cycle); 713 714 static struct attribute *silicom_attrs[] = { 715 &dev_attr_efuse_status.attr, 716 &dev_attr_uc_version.attr, 717 &dev_attr_power_cycle.attr, 718 NULL, 719 }; 720 ATTRIBUTE_GROUPS(silicom); 721 722 static struct platform_driver silicom_platform_driver = { 723 .driver = { 724 .name = "silicom-platform", 725 .dev_groups = silicom_groups, 726 }, 727 }; 728 729 static int __init silicom_mc_leds_register(struct device *dev, 730 const struct led_classdev_mc *mc_leds) 731 { 732 int size = sizeof(struct mc_subled); 733 struct led_classdev_mc *led; 734 int i, err; 735 736 for (i = 0; mc_leds[i].led_cdev.name; i++) { 737 738 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 739 if (!led) 740 return -ENOMEM; 741 memcpy(led, &mc_leds[i], sizeof(*led)); 742 743 led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL); 744 if (!led->subled_info) 745 return -ENOMEM; 746 memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size); 747 748 err = devm_led_classdev_multicolor_register(dev, led); 749 if (err) 750 return err; 751 } 752 753 return 0; 754 } 755 756 static u32 rpm_get(void) 757 { 758 u32 reg; 759 760 mutex_lock(&mec_io_mutex); 761 /* Select memory region */ 762 outb(IO_REG_BANK, EC_ADDR_MSB); 763 outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); 764 reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 765 mutex_unlock(&mec_io_mutex); 766 767 return reg; 768 } 769 770 static u32 temp_get(void) 771 { 772 u32 reg; 773 774 mutex_lock(&mec_io_mutex); 775 /* Select memory region */ 776 outb(IO_REG_BANK, EC_ADDR_MSB); 777 outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); 778 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 779 mutex_unlock(&mec_io_mutex); 780 781 return FIELD_GET(MEC_TEMP_LOC, reg) * 100; 782 } 783 784 static umode_t silicom_fan_control_fan_is_visible(const u32 attr) 785 { 786 switch (attr) { 787 case hwmon_fan_input: 788 case hwmon_fan_label: 789 return 0444; 790 default: 791 return 0; 792 } 793 } 794 795 static umode_t silicom_fan_control_temp_is_visible(const u32 attr) 796 { 797 switch (attr) { 798 case hwmon_temp_input: 799 case hwmon_temp_label: 800 return 0444; 801 default: 802 return 0; 803 } 804 } 805 806 static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val) 807 { 808 switch (attr) { 809 case hwmon_fan_input: 810 *val = rpm_get(); 811 return 0; 812 default: 813 return -EOPNOTSUPP; 814 } 815 } 816 817 static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val) 818 { 819 switch (attr) { 820 case hwmon_temp_input: 821 *val = temp_get(); 822 return 0; 823 default: 824 return -EOPNOTSUPP; 825 } 826 } 827 828 static umode_t silicom_fan_control_is_visible(const void *data, 829 enum hwmon_sensor_types type, 830 u32 attr, int channel) 831 { 832 switch (type) { 833 case hwmon_fan: 834 return silicom_fan_control_fan_is_visible(attr); 835 case hwmon_temp: 836 return silicom_fan_control_temp_is_visible(attr); 837 default: 838 return 0; 839 } 840 } 841 842 static int silicom_fan_control_read(struct device *dev, 843 enum hwmon_sensor_types type, 844 u32 attr, int channel, 845 long *val) 846 { 847 switch (type) { 848 case hwmon_fan: 849 return silicom_fan_control_read_fan(dev, attr, val); 850 case hwmon_temp: 851 return silicom_fan_control_read_temp(dev, attr, val); 852 default: 853 return -EOPNOTSUPP; 854 } 855 } 856 857 static int silicom_fan_control_read_labels(struct device *dev, 858 enum hwmon_sensor_types type, 859 u32 attr, int channel, 860 const char **str) 861 { 862 switch (type) { 863 case hwmon_fan: 864 *str = "Silicom_platform: Fan Speed"; 865 return 0; 866 case hwmon_temp: 867 *str = "Silicom_platform: Thermostat Sensor"; 868 return 0; 869 default: 870 return -EOPNOTSUPP; 871 } 872 } 873 874 static const struct hwmon_ops silicom_fan_control_hwmon_ops = { 875 .is_visible = silicom_fan_control_is_visible, 876 .read = silicom_fan_control_read, 877 .read_string = silicom_fan_control_read_labels, 878 }; 879 880 static const struct hwmon_chip_info silicom_chip_info = { 881 .ops = &silicom_fan_control_hwmon_ops, 882 .info = silicom_fan_control_info, 883 }; 884 885 static int __init silicom_platform_probe(struct platform_device *device) 886 { 887 struct device *hwmon_dev; 888 u8 magic, ver; 889 int err; 890 891 if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) { 892 dev_err(&device->dev, "couldn't reserve MEC io ports\n"); 893 return -EBUSY; 894 } 895 896 /* Sanity check magic number read for EC */ 897 outb(IO_REG_BANK, MEC_ADDR); 898 magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 899 ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI)); 900 dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver); 901 902 if (magic != SILICOM_MEC_MAGIC) { 903 dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic); 904 return -ENODEV; 905 } 906 907 err = silicom_mc_leds_register(&device->dev, silicom_led_info); 908 if (err) { 909 dev_err(&device->dev, "Failed to register LEDs\n"); 910 return err; 911 } 912 913 err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip, 914 silicom_gpio_channels); 915 if (err) { 916 dev_err(&device->dev, "Failed to register gpiochip: %d\n", err); 917 return err; 918 } 919 920 hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL, 921 &silicom_chip_info, NULL); 922 err = PTR_ERR_OR_ZERO(hwmon_dev); 923 if (err) { 924 dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err); 925 return err; 926 } 927 928 return err; 929 } 930 931 static int __init silicom_platform_info_init(const struct dmi_system_id *id) 932 { 933 struct silicom_platform_info *info = id->driver_data; 934 935 silicom_led_info = info->led_info; 936 silicom_gpio_channels = info->gpio_channels; 937 silicom_gpiochip = info->gpiochip; 938 silicom_gpiochip->ngpio = info->ngpio; 939 940 return 1; 941 } 942 943 static const struct dmi_system_id silicom_dmi_ids[] __initconst = { 944 { 945 .callback = silicom_platform_info_init, 946 .ident = "Silicom Cordoba (Generic)", 947 .matches = { 948 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 949 DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"), 950 }, 951 .driver_data = &silicom_generic_cordoba_info, 952 }, 953 { 954 .callback = silicom_platform_info_init, 955 .ident = "Silicom Cordoba (Generic)", 956 .matches = { 957 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 958 DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"), 959 }, 960 .driver_data = &silicom_generic_cordoba_info, 961 }, 962 { 963 .callback = silicom_platform_info_init, 964 .ident = "Silicom Cordoba (plat_0222)", 965 .matches = { 966 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 967 DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"), 968 }, 969 .driver_data = &silicom_plat_0222_cordoba_info, 970 }, 971 { }, 972 }; 973 MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids); 974 975 static int __init silicom_platform_init(void) 976 { 977 if (!dmi_check_system(silicom_dmi_ids)) { 978 pr_err("No DMI match for this platform\n"); 979 return -ENODEV; 980 } 981 silicom_platform_dev = platform_create_bundle(&silicom_platform_driver, 982 silicom_platform_probe, 983 NULL, 0, NULL, 0); 984 985 return PTR_ERR_OR_ZERO(silicom_platform_dev); 986 } 987 988 static void __exit silicom_platform_exit(void) 989 { 990 platform_device_unregister(silicom_platform_dev); 991 platform_driver_unregister(&silicom_platform_driver); 992 } 993 994 module_init(silicom_platform_init); 995 module_exit(silicom_platform_exit); 996 997 MODULE_LICENSE("GPL"); 998 MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>"); 999 MODULE_DESCRIPTION("Platform driver for Silicom network appliances"); 1000