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