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