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 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 
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 
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  */
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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