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
silicom_mec_port_get(unsigned int offset)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
silicom_mec_led_get(int channel)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
silicom_mec_port_set(int channel,int on)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
silicom_mec_led_mc_brightness_get(struct led_classdev * led_cdev)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
silicom_mec_led_mc_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)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
silicom_gpio_get_direction(struct gpio_chip * gc,unsigned int offset)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
silicom_gpio_direction_input(struct gpio_chip * gc,unsigned int offset)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
silicom_gpio_set(struct gpio_chip * gc,unsigned int offset,int value)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
silicom_gpio_direction_output(struct gpio_chip * gc,unsigned int offset,int value)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
silicom_gpio_get(struct gpio_chip * gc,unsigned int offset)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 */
efuse_status_show(struct device * dev,struct device_attribute * attr,char * buf)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
uc_version_show(struct device * dev,struct device_attribute * attr,char * buf)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
power_cycle_show(struct device * dev,struct device_attribute * attr,char * buf)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
powercycle_uc(void)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
power_cycle_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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
silicom_mc_leds_register(struct device * dev,const struct led_classdev_mc * mc_leds)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
rpm_get(void)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
temp_get(void)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
silicom_fan_control_fan_is_visible(const u32 attr)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
silicom_fan_control_temp_is_visible(const u32 attr)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
silicom_fan_control_read_fan(struct device * dev,u32 attr,long * val)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
silicom_fan_control_read_temp(struct device * dev,u32 attr,long * val)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
silicom_fan_control_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)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
silicom_fan_control_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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
silicom_fan_control_read_labels(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)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
silicom_platform_probe(struct platform_device * device)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
silicom_platform_info_init(const struct dmi_system_id * id)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
silicom_platform_init(void)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
silicom_platform_exit(void)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