xref: /linux/drivers/platform/x86/silicom-platform.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
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