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 int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset, 249 int value) 250 { 251 int direction = silicom_gpio_get_direction(gc, offset); 252 u8 *channels = gpiochip_get_data(gc); 253 int channel = channels[offset]; 254 255 if (direction == GPIO_LINE_DIRECTION_IN) 256 return -EPERM; 257 258 silicom_mec_port_set(channel, !value); 259 260 return 0; 261 } 262 263 static int silicom_gpio_direction_output(struct gpio_chip *gc, 264 unsigned int offset, 265 int value) 266 { 267 int direction = silicom_gpio_get_direction(gc, offset); 268 269 if (direction == GPIO_LINE_DIRECTION_IN) 270 return -EINVAL; 271 272 silicom_gpio_set(gc, offset, value); 273 274 return 0; 275 } 276 277 static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset) 278 { 279 u8 *channels = gpiochip_get_data(gc); 280 int channel = channels[offset]; 281 282 return silicom_mec_port_get(channel); 283 } 284 285 static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = { 286 { 287 .color_index = LED_COLOR_ID_WHITE, 288 .brightness = 1, 289 .intensity = 0, 290 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7), 291 }, 292 { 293 .color_index = LED_COLOR_ID_YELLOW, 294 .brightness = 1, 295 .intensity = 0, 296 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6), 297 }, 298 { 299 .color_index = LED_COLOR_ID_RED, 300 .brightness = 1, 301 .intensity = 0, 302 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5), 303 }, 304 }; 305 306 static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = { 307 { 308 .color_index = LED_COLOR_ID_WHITE, 309 .brightness = 1, 310 .intensity = 0, 311 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4), 312 }, 313 { 314 .color_index = LED_COLOR_ID_AMBER, 315 .brightness = 1, 316 .intensity = 0, 317 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3), 318 }, 319 { 320 .color_index = LED_COLOR_ID_RED, 321 .brightness = 1, 322 .intensity = 0, 323 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2), 324 }, 325 }; 326 327 static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = { 328 { 329 .color_index = LED_COLOR_ID_RED, 330 .brightness = 1, 331 .intensity = 0, 332 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1), 333 }, 334 { 335 .color_index = LED_COLOR_ID_GREEN, 336 .brightness = 1, 337 .intensity = 0, 338 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0), 339 }, 340 { 341 .color_index = LED_COLOR_ID_BLUE, 342 .brightness = 1, 343 .intensity = 0, 344 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7), 345 }, 346 { 347 .color_index = LED_COLOR_ID_YELLOW, 348 .brightness = 1, 349 .intensity = 0, 350 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6), 351 }, 352 }; 353 354 static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = { 355 { 356 .color_index = LED_COLOR_ID_RED, 357 .brightness = 1, 358 .intensity = 0, 359 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5), 360 }, 361 { 362 .color_index = LED_COLOR_ID_GREEN, 363 .brightness = 1, 364 .intensity = 0, 365 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4), 366 }, 367 { 368 .color_index = LED_COLOR_ID_BLUE, 369 .brightness = 1, 370 .intensity = 0, 371 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3), 372 }, 373 { 374 .color_index = LED_COLOR_ID_YELLOW, 375 .brightness = 1, 376 .intensity = 0, 377 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2), 378 }, 379 }; 380 381 static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = { 382 { 383 .color_index = LED_COLOR_ID_RED, 384 .brightness = 1, 385 .intensity = 0, 386 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1), 387 }, 388 { 389 .color_index = LED_COLOR_ID_GREEN, 390 .brightness = 1, 391 .intensity = 0, 392 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0), 393 }, 394 { 395 .color_index = LED_COLOR_ID_BLUE, 396 .brightness = 1, 397 .intensity = 0, 398 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1), 399 }, 400 { 401 .color_index = LED_COLOR_ID_YELLOW, 402 .brightness = 1, 403 .intensity = 0, 404 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0), 405 }, 406 }; 407 408 static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = { 409 { 410 .led_cdev = { 411 .name = "platled::wan", 412 .brightness = 0, 413 .max_brightness = 1, 414 .brightness_set = silicom_mec_led_mc_brightness_set, 415 .brightness_get = silicom_mec_led_mc_brightness_get, 416 }, 417 .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info), 418 .subled_info = plat_0222_wan_mc_subled_info, 419 }, 420 { 421 .led_cdev = { 422 .name = "platled::sys", 423 .brightness = 0, 424 .max_brightness = 1, 425 .brightness_set = silicom_mec_led_mc_brightness_set, 426 .brightness_get = silicom_mec_led_mc_brightness_get, 427 }, 428 .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info), 429 .subled_info = plat_0222_sys_mc_subled_info, 430 }, 431 { 432 .led_cdev = { 433 .name = "platled::stat1", 434 .brightness = 0, 435 .max_brightness = 1, 436 .brightness_set = silicom_mec_led_mc_brightness_set, 437 .brightness_get = silicom_mec_led_mc_brightness_get, 438 }, 439 .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info), 440 .subled_info = plat_0222_stat1_mc_subled_info, 441 }, 442 { 443 .led_cdev = { 444 .name = "platled::stat2", 445 .brightness = 0, 446 .max_brightness = 1, 447 .brightness_set = silicom_mec_led_mc_brightness_set, 448 .brightness_get = silicom_mec_led_mc_brightness_get, 449 }, 450 .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info), 451 .subled_info = plat_0222_stat2_mc_subled_info, 452 }, 453 { 454 .led_cdev = { 455 .name = "platled::stat3", 456 .brightness = 0, 457 .max_brightness = 1, 458 .brightness_set = silicom_mec_led_mc_brightness_set, 459 .brightness_get = silicom_mec_led_mc_brightness_get, 460 }, 461 .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info), 462 .subled_info = plat_0222_stat3_mc_subled_info, 463 }, 464 { }, 465 }; 466 467 static struct gpio_chip silicom_gpio_chip = { 468 .label = "silicom-gpio", 469 .get_direction = silicom_gpio_get_direction, 470 .direction_input = silicom_gpio_direction_input, 471 .direction_output = silicom_gpio_direction_output, 472 .get = silicom_gpio_get, 473 .set_rv = silicom_gpio_set, 474 .base = -1, 475 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 476 .names = plat_0222_gpio_names, 477 /* 478 * We're using a mutex to protect the indirect access, so we can sleep 479 * if the lock blocks 480 */ 481 .can_sleep = true, 482 }; 483 484 static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = { 485 .io_base = MEC_IO_BASE, 486 .io_len = MEC_IO_LEN, 487 .led_info = plat_0222_mc_led_info, 488 .gpiochip = &silicom_gpio_chip, 489 .gpio_channels = plat_0222_gpio_channels, 490 /* 491 * The original generic cordoba does not have the last 4 outputs of the 492 * plat_0222 variant, the rest are the same, so use the same longer list, 493 * but ignore the last entries here 494 */ 495 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 496 497 }; 498 499 static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = { 500 { 501 .color_index = LED_COLOR_ID_RED, 502 .brightness = 1, 503 .intensity = 0, 504 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6), 505 }, 506 { 507 .color_index = LED_COLOR_ID_GREEN, 508 .brightness = 1, 509 .intensity = 0, 510 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5), 511 }, 512 { 513 .color_index = LED_COLOR_ID_BLUE, 514 .brightness = 1, 515 .intensity = 0, 516 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7), 517 }, 518 { 519 .color_index = LED_COLOR_ID_AMBER, 520 .brightness = 1, 521 .intensity = 0, 522 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4), 523 }, 524 }; 525 526 static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = { 527 { 528 .color_index = LED_COLOR_ID_RED, 529 .brightness = 1, 530 .intensity = 0, 531 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7), 532 }, 533 { 534 .color_index = LED_COLOR_ID_GREEN, 535 .brightness = 1, 536 .intensity = 0, 537 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4), 538 }, 539 { 540 .color_index = LED_COLOR_ID_BLUE, 541 .brightness = 1, 542 .intensity = 0, 543 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3), 544 }, 545 { 546 .color_index = LED_COLOR_ID_AMBER, 547 .brightness = 1, 548 .intensity = 0, 549 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6), 550 }, 551 }; 552 553 static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = { 554 { 555 .color_index = LED_COLOR_ID_RED, 556 .brightness = 1, 557 .intensity = 0, 558 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2), 559 }, 560 { 561 .color_index = LED_COLOR_ID_GREEN, 562 .brightness = 1, 563 .intensity = 0, 564 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1), 565 }, 566 { 567 .color_index = LED_COLOR_ID_BLUE, 568 .brightness = 1, 569 .intensity = 0, 570 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0), 571 }, 572 { 573 .color_index = LED_COLOR_ID_AMBER, 574 .brightness = 1, 575 .intensity = 0, 576 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5), 577 }, 578 }; 579 580 static struct led_classdev_mc cordoba_mc_led_info[] __initdata = { 581 { 582 .led_cdev = { 583 .name = "platled::fp_left", 584 .brightness = 0, 585 .max_brightness = 1, 586 .brightness_set = silicom_mec_led_mc_brightness_set, 587 .brightness_get = silicom_mec_led_mc_brightness_get, 588 }, 589 .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info), 590 .subled_info = cordoba_fp_left_mc_subled_info, 591 }, 592 { 593 .led_cdev = { 594 .name = "platled::fp_center", 595 .brightness = 0, 596 .max_brightness = 1, 597 .brightness_set = silicom_mec_led_mc_brightness_set, 598 .brightness_get = silicom_mec_led_mc_brightness_get, 599 }, 600 .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info), 601 .subled_info = cordoba_fp_center_mc_subled_info, 602 }, 603 { 604 .led_cdev = { 605 .name = "platled::fp_right", 606 .brightness = 0, 607 .max_brightness = 1, 608 .brightness_set = silicom_mec_led_mc_brightness_set, 609 .brightness_get = silicom_mec_led_mc_brightness_get, 610 }, 611 .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info), 612 .subled_info = cordoba_fp_right_mc_subled_info, 613 }, 614 { }, 615 }; 616 617 static struct silicom_platform_info silicom_generic_cordoba_info __initdata = { 618 .io_base = MEC_IO_BASE, 619 .io_len = MEC_IO_LEN, 620 .led_info = cordoba_mc_led_info, 621 .gpiochip = &silicom_gpio_chip, 622 .gpio_channels = plat_0222_gpio_channels, 623 .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), 624 }; 625 626 /* 627 * sysfs interface 628 */ 629 static ssize_t efuse_status_show(struct device *dev, 630 struct device_attribute *attr, 631 char *buf) 632 { 633 u32 reg; 634 635 mutex_lock(&mec_io_mutex); 636 /* Select memory region */ 637 outb(IO_REG_BANK, EC_ADDR_MSB); 638 outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB); 639 640 /* Get current data from the address */ 641 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 642 mutex_unlock(&mec_io_mutex); 643 644 efuse_status = reg & 0x1; 645 646 return sysfs_emit(buf, "%u\n", efuse_status); 647 } 648 static DEVICE_ATTR_RO(efuse_status); 649 650 static ssize_t uc_version_show(struct device *dev, 651 struct device_attribute *attr, 652 char *buf) 653 { 654 int uc_version; 655 u32 reg; 656 657 mutex_lock(&mec_io_mutex); 658 outb(IO_REG_BANK, EC_ADDR_MSB); 659 outb(DEFAULT_CHAN_LO, EC_ADDR_LSB); 660 661 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 662 mutex_unlock(&mec_io_mutex); 663 uc_version = FIELD_GET(MEC_VERSION_LOC, reg); 664 if (uc_version >= 192) 665 return -EINVAL; 666 667 uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 + 668 FIELD_GET(MEC_VERSION_MINOR, reg); 669 670 mec_uc_version = uc_version; 671 672 return sysfs_emit(buf, "%u\n", mec_uc_version); 673 } 674 static DEVICE_ATTR_RO(uc_version); 675 676 static ssize_t power_cycle_show(struct device *dev, 677 struct device_attribute *attr, 678 char *buf) 679 { 680 return sysfs_emit(buf, "%u\n", power_cycle); 681 } 682 683 static void powercycle_uc(void) 684 { 685 /* Select memory region */ 686 outb(IO_REG_BANK, EC_ADDR_MSB); 687 outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB); 688 689 /* Set to 1 for current data from the address */ 690 outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 691 } 692 693 static ssize_t power_cycle_store(struct device *dev, 694 struct device_attribute *attr, 695 const char *buf, size_t count) 696 { 697 int rc; 698 unsigned int power_cycle_cmd; 699 700 rc = kstrtou32(buf, 0, &power_cycle_cmd); 701 if (rc) 702 return -EINVAL; 703 704 if (power_cycle_cmd > 0) { 705 mutex_lock(&mec_io_mutex); 706 power_cycle = power_cycle_cmd; 707 powercycle_uc(); 708 mutex_unlock(&mec_io_mutex); 709 } 710 711 return count; 712 } 713 static DEVICE_ATTR_RW(power_cycle); 714 715 static struct attribute *silicom_attrs[] = { 716 &dev_attr_efuse_status.attr, 717 &dev_attr_uc_version.attr, 718 &dev_attr_power_cycle.attr, 719 NULL, 720 }; 721 ATTRIBUTE_GROUPS(silicom); 722 723 static struct platform_driver silicom_platform_driver = { 724 .driver = { 725 .name = "silicom-platform", 726 .dev_groups = silicom_groups, 727 }, 728 }; 729 730 static int __init silicom_mc_leds_register(struct device *dev, 731 const struct led_classdev_mc *mc_leds) 732 { 733 int size = sizeof(struct mc_subled); 734 struct led_classdev_mc *led; 735 int i, err; 736 737 for (i = 0; mc_leds[i].led_cdev.name; i++) { 738 739 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 740 if (!led) 741 return -ENOMEM; 742 memcpy(led, &mc_leds[i], sizeof(*led)); 743 744 led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL); 745 if (!led->subled_info) 746 return -ENOMEM; 747 memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size); 748 749 err = devm_led_classdev_multicolor_register(dev, led); 750 if (err) 751 return err; 752 } 753 754 return 0; 755 } 756 757 static u32 rpm_get(void) 758 { 759 u32 reg; 760 761 mutex_lock(&mec_io_mutex); 762 /* Select memory region */ 763 outb(IO_REG_BANK, EC_ADDR_MSB); 764 outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); 765 reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 766 mutex_unlock(&mec_io_mutex); 767 768 return reg; 769 } 770 771 static u32 temp_get(void) 772 { 773 u32 reg; 774 775 mutex_lock(&mec_io_mutex); 776 /* Select memory region */ 777 outb(IO_REG_BANK, EC_ADDR_MSB); 778 outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); 779 reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 780 mutex_unlock(&mec_io_mutex); 781 782 return FIELD_GET(MEC_TEMP_LOC, reg) * 100; 783 } 784 785 static umode_t silicom_fan_control_fan_is_visible(const u32 attr) 786 { 787 switch (attr) { 788 case hwmon_fan_input: 789 case hwmon_fan_label: 790 return 0444; 791 default: 792 return 0; 793 } 794 } 795 796 static umode_t silicom_fan_control_temp_is_visible(const u32 attr) 797 { 798 switch (attr) { 799 case hwmon_temp_input: 800 case hwmon_temp_label: 801 return 0444; 802 default: 803 return 0; 804 } 805 } 806 807 static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val) 808 { 809 switch (attr) { 810 case hwmon_fan_input: 811 *val = rpm_get(); 812 return 0; 813 default: 814 return -EOPNOTSUPP; 815 } 816 } 817 818 static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val) 819 { 820 switch (attr) { 821 case hwmon_temp_input: 822 *val = temp_get(); 823 return 0; 824 default: 825 return -EOPNOTSUPP; 826 } 827 } 828 829 static umode_t silicom_fan_control_is_visible(const void *data, 830 enum hwmon_sensor_types type, 831 u32 attr, int channel) 832 { 833 switch (type) { 834 case hwmon_fan: 835 return silicom_fan_control_fan_is_visible(attr); 836 case hwmon_temp: 837 return silicom_fan_control_temp_is_visible(attr); 838 default: 839 return 0; 840 } 841 } 842 843 static int silicom_fan_control_read(struct device *dev, 844 enum hwmon_sensor_types type, 845 u32 attr, int channel, 846 long *val) 847 { 848 switch (type) { 849 case hwmon_fan: 850 return silicom_fan_control_read_fan(dev, attr, val); 851 case hwmon_temp: 852 return silicom_fan_control_read_temp(dev, attr, val); 853 default: 854 return -EOPNOTSUPP; 855 } 856 } 857 858 static int silicom_fan_control_read_labels(struct device *dev, 859 enum hwmon_sensor_types type, 860 u32 attr, int channel, 861 const char **str) 862 { 863 switch (type) { 864 case hwmon_fan: 865 *str = "Silicom_platform: Fan Speed"; 866 return 0; 867 case hwmon_temp: 868 *str = "Silicom_platform: Thermostat Sensor"; 869 return 0; 870 default: 871 return -EOPNOTSUPP; 872 } 873 } 874 875 static const struct hwmon_ops silicom_fan_control_hwmon_ops = { 876 .is_visible = silicom_fan_control_is_visible, 877 .read = silicom_fan_control_read, 878 .read_string = silicom_fan_control_read_labels, 879 }; 880 881 static const struct hwmon_chip_info silicom_chip_info = { 882 .ops = &silicom_fan_control_hwmon_ops, 883 .info = silicom_fan_control_info, 884 }; 885 886 static int __init silicom_platform_probe(struct platform_device *device) 887 { 888 struct device *hwmon_dev; 889 u8 magic, ver; 890 int err; 891 892 if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) { 893 dev_err(&device->dev, "couldn't reserve MEC io ports\n"); 894 return -EBUSY; 895 } 896 897 /* Sanity check magic number read for EC */ 898 outb(IO_REG_BANK, MEC_ADDR); 899 magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); 900 ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI)); 901 dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver); 902 903 if (magic != SILICOM_MEC_MAGIC) { 904 dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic); 905 return -ENODEV; 906 } 907 908 err = silicom_mc_leds_register(&device->dev, silicom_led_info); 909 if (err) { 910 dev_err(&device->dev, "Failed to register LEDs\n"); 911 return err; 912 } 913 914 err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip, 915 silicom_gpio_channels); 916 if (err) { 917 dev_err(&device->dev, "Failed to register gpiochip: %d\n", err); 918 return err; 919 } 920 921 hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL, 922 &silicom_chip_info, NULL); 923 err = PTR_ERR_OR_ZERO(hwmon_dev); 924 if (err) { 925 dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err); 926 return err; 927 } 928 929 return err; 930 } 931 932 static int __init silicom_platform_info_init(const struct dmi_system_id *id) 933 { 934 struct silicom_platform_info *info = id->driver_data; 935 936 silicom_led_info = info->led_info; 937 silicom_gpio_channels = info->gpio_channels; 938 silicom_gpiochip = info->gpiochip; 939 silicom_gpiochip->ngpio = info->ngpio; 940 941 return 1; 942 } 943 944 static const struct dmi_system_id silicom_dmi_ids[] __initconst = { 945 { 946 .callback = silicom_platform_info_init, 947 .ident = "Silicom Cordoba (Generic)", 948 .matches = { 949 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 950 DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"), 951 }, 952 .driver_data = &silicom_generic_cordoba_info, 953 }, 954 { 955 .callback = silicom_platform_info_init, 956 .ident = "Silicom Cordoba (Generic)", 957 .matches = { 958 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 959 DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"), 960 }, 961 .driver_data = &silicom_generic_cordoba_info, 962 }, 963 { 964 .callback = silicom_platform_info_init, 965 .ident = "Silicom Cordoba (plat_0222)", 966 .matches = { 967 DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), 968 DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"), 969 }, 970 .driver_data = &silicom_plat_0222_cordoba_info, 971 }, 972 { }, 973 }; 974 MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids); 975 976 static int __init silicom_platform_init(void) 977 { 978 if (!dmi_check_system(silicom_dmi_ids)) { 979 pr_err("No DMI match for this platform\n"); 980 return -ENODEV; 981 } 982 silicom_platform_dev = platform_create_bundle(&silicom_platform_driver, 983 silicom_platform_probe, 984 NULL, 0, NULL, 0); 985 986 return PTR_ERR_OR_ZERO(silicom_platform_dev); 987 } 988 989 static void __exit silicom_platform_exit(void) 990 { 991 platform_device_unregister(silicom_platform_dev); 992 platform_driver_unregister(&silicom_platform_driver); 993 } 994 995 module_init(silicom_platform_init); 996 module_exit(silicom_platform_exit); 997 998 MODULE_LICENSE("GPL"); 999 MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>"); 1000 MODULE_DESCRIPTION("Platform driver for Silicom network appliances"); 1001