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