Loading...
Note: File does not exist in v4.6.
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
54static DEFINE_MUTEX(mec_io_mutex);
55static unsigned int efuse_status;
56static unsigned int mec_uc_version;
57static unsigned int power_cycle;
58
59static 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
65struct 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
74static 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
111static 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
148static struct platform_device *silicom_platform_dev;
149static struct led_classdev_mc *silicom_led_info __initdata;
150static struct gpio_chip *silicom_gpiochip __initdata;
151static u8 *silicom_gpio_channels __initdata;
152
153static 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
170static 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
176static 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
198static 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
215static 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
228static 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
240static 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
248static 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
267static 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
281static 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
289static 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
310static 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
331static 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
358static 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
385static 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
412static 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
471static 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
488static 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
503static 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
530static 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
557static 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
584static 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
621static 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 */
633static 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}
652static DEVICE_ATTR_RO(efuse_status);
653
654static 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}
678static DEVICE_ATTR_RO(uc_version);
679
680static 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
687static 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
697static 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}
717static DEVICE_ATTR_RW(power_cycle);
718
719static 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};
725ATTRIBUTE_GROUPS(silicom);
726
727static struct platform_driver silicom_platform_driver = {
728 .driver = {
729 .name = "silicom-platform",
730 .dev_groups = silicom_groups,
731 },
732};
733
734static 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
761static 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
775static 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
789static 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
800static 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
811static 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
822static 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
833static 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
847static 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
862static 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
879static 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
885static const struct hwmon_chip_info silicom_chip_info = {
886 .ops = &silicom_fan_control_hwmon_ops,
887 .info = silicom_fan_control_info,
888};
889
890static 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
936static 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
948static 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};
978MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
979
980static 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
993static void __exit silicom_platform_exit(void)
994{
995 platform_device_unregister(silicom_platform_dev);
996 platform_driver_unregister(&silicom_platform_driver);
997}
998
999module_init(silicom_platform_init);
1000module_exit(silicom_platform_exit);
1001
1002MODULE_LICENSE("GPL");
1003MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
1004MODULE_DESCRIPTION("Platform driver for Silicom network appliances");