File Index
Files and directories
Sign in
FILE

silicom-platform.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/silicom-platform.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2//
  3// silicom-platform.c - Silicom MEC170x platform driver
  4//
  5// Copyright (C) 2023 Henry Shi <henrys@silicom-usa.com>
  6#include <linux/bitfield.h>
  7#include <linux/bits.h>
  8#include <linux/dmi.h>
  9#include <linux/hwmon.h>
 10#include <linux/init.h>
 11#include <linux/ioport.h>
 12#include <linux/io.h>
 13#include <linux/kernel.h>
 14#include <linux/kobject.h>
 15#include <linux/led-class-multicolor.h>
 16#include <linux/module.h>
 17#include <linux/mutex.h>
 18#include <linux/platform_device.h>
 19#include <linux/string.h>
 20#include <linux/sysfs.h>
 21#include <linux/units.h>
 22
 23#include <linux/gpio/driver.h>
 24
 25#define MEC_POWER_CYCLE_ADDR 0x24
 26#define MEC_EFUSE_LSB_ADDR   0x28
 27#define MEC_GPIO_IN_POS      0x08
 28#define MEC_IO_BASE          0x0800
 29#define MEC_IO_LEN           0x8
 30#define IO_REG_BANK          0x0
 31#define DEFAULT_CHAN_LO      0
 32#define DEFAULT_CHAN_HI      0
 33#define DEFAULT_CHAN_LO_T    0xc
 34#define MEC_ADDR             (MEC_IO_BASE + 0x02)
 35#define EC_ADDR_LSB          MEC_ADDR
 36#define SILICOM_MEC_MAGIC    0x5a
 37
 38#define MEC_PORT_CHANNEL_MASK GENMASK(2, 0)
 39#define MEC_PORT_DWORD_OFFSET GENMASK(31, 3)
 40#define MEC_DATA_OFFSET_MASK  GENMASK(1, 0)
 41#define MEC_PORT_OFFSET_MASK  GENMASK(7, 2)
 42
 43#define MEC_TEMP_LOC          GENMASK(31, 16)
 44#define MEC_VERSION_LOC       GENMASK(15, 8)
 45#define MEC_VERSION_MAJOR     GENMASK(15, 14)
 46#define MEC_VERSION_MINOR     GENMASK(13, 8)
 47
 48#define EC_ADDR_MSB           (MEC_IO_BASE + 0x3)
 49#define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset))
 50
 51#define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
 52#define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
 53
 54static DEFINE_MUTEX(mec_io_mutex);
 55static unsigned int efuse_status;
 56static unsigned int mec_uc_version;
 57static unsigned int power_cycle;
 58
 59static const struct hwmon_channel_info *silicom_fan_control_info[] = {
 60	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
 61	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
 62	NULL
 63};
 64
 65struct silicom_platform_info {
 66	int io_base;
 67	int io_len;
 68	struct led_classdev_mc *led_info;
 69	struct gpio_chip *gpiochip;
 70	u8 *gpio_channels;
 71	u16 ngpio;
 72};
 73
 74static const char * const plat_0222_gpio_names[] = {
 75	"AUTOM0_SFP_TX_FAULT",
 76	"SLOT2_LED_OUT",
 77	"SIM_M2_SLOT2_B_DET",
 78	"SIM_M2_SLOT2_A_DET",
 79	"SLOT1_LED_OUT",
 80	"SIM_M2_SLOT1_B_DET",
 81	"SIM_M2_SLOT1_A_DET",
 82	"SLOT0_LED_OUT",
 83	"WAN_SFP0_RX_LOS",
 84	"WAN_SFP0_PRSNT_N",
 85	"WAN_SFP0_TX_FAULT",
 86	"AUTOM1_SFP_RX_LOS",
 87	"AUTOM1_SFP_PRSNT_N",
 88	"AUTOM1_SFP_TX_FAULT",
 89	"AUTOM0_SFP_RX_LOS",
 90	"AUTOM0_SFP_PRSNT_N",
 91	"WAN_SFP1_RX_LOS",
 92	"WAN_SFP1_PRSNT_N",
 93	"WAN_SFP1_TX_FAULT",
 94	"SIM_M2_SLOT1_MUX_SEL",
 95	"W_DISABLE_M2_SLOT1_N",
 96	"W_DISABLE_MPCIE_SLOT0_N",
 97	"W_DISABLE_M2_SLOT0_N",
 98	"BT_COMMAND_MODE",
 99	"WAN_SFP1_TX_DISABLE",
100	"WAN_SFP0_TX_DISABLE",
101	"AUTOM1_SFP_TX_DISABLE",
102	"AUTOM0_SFP_TX_DISABLE",
103	"SIM_M2_SLOT2_MUX_SEL",
104	"W_DISABLE_M2_SLOT2_N",
105	"RST_CTL_M2_SLOT_1_N",
106	"RST_CTL_M2_SLOT_2_N",
107	"PM_USB_PWR_EN_BOT",
108	"PM_USB_PWR_EN_TOP",
109};
110
111static u8 plat_0222_gpio_channels[] = {
112	OFFSET_BIT_TO_CHANNEL(0x00, 0),
113	OFFSET_BIT_TO_CHANNEL(0x00, 1),
114	OFFSET_BIT_TO_CHANNEL(0x00, 2),
115	OFFSET_BIT_TO_CHANNEL(0x00, 3),
116	OFFSET_BIT_TO_CHANNEL(0x00, 4),
117	OFFSET_BIT_TO_CHANNEL(0x00, 5),
118	OFFSET_BIT_TO_CHANNEL(0x00, 6),
119	OFFSET_BIT_TO_CHANNEL(0x00, 7),
120	OFFSET_BIT_TO_CHANNEL(0x01, 0),
121	OFFSET_BIT_TO_CHANNEL(0x01, 1),
122	OFFSET_BIT_TO_CHANNEL(0x01, 2),
123	OFFSET_BIT_TO_CHANNEL(0x01, 3),
124	OFFSET_BIT_TO_CHANNEL(0x01, 4),
125	OFFSET_BIT_TO_CHANNEL(0x01, 5),
126	OFFSET_BIT_TO_CHANNEL(0x01, 6),
127	OFFSET_BIT_TO_CHANNEL(0x01, 7),
128	OFFSET_BIT_TO_CHANNEL(0x02, 0),
129	OFFSET_BIT_TO_CHANNEL(0x02, 1),
130	OFFSET_BIT_TO_CHANNEL(0x02, 2),
131	OFFSET_BIT_TO_CHANNEL(0x09, 0),
132	OFFSET_BIT_TO_CHANNEL(0x09, 1),
133	OFFSET_BIT_TO_CHANNEL(0x09, 2),
134	OFFSET_BIT_TO_CHANNEL(0x09, 3),
135	OFFSET_BIT_TO_CHANNEL(0x0a, 0),
136	OFFSET_BIT_TO_CHANNEL(0x0a, 1),
137	OFFSET_BIT_TO_CHANNEL(0x0a, 2),
138	OFFSET_BIT_TO_CHANNEL(0x0a, 3),
139	OFFSET_BIT_TO_CHANNEL(0x0a, 4),
140	OFFSET_BIT_TO_CHANNEL(0x0a, 5),
141	OFFSET_BIT_TO_CHANNEL(0x0a, 6),
142	OFFSET_BIT_TO_CHANNEL(0x0b, 0),
143	OFFSET_BIT_TO_CHANNEL(0x0b, 1),
144	OFFSET_BIT_TO_CHANNEL(0x0b, 2),
145	OFFSET_BIT_TO_CHANNEL(0x0b, 3),
146};
147
148static struct platform_device *silicom_platform_dev;
149static struct led_classdev_mc *silicom_led_info __initdata;
150static struct gpio_chip *silicom_gpiochip __initdata;
151static u8 *silicom_gpio_channels __initdata;
152
153static int silicom_mec_port_get(unsigned int offset)
154{
155	unsigned short mec_data_addr;
156	unsigned short mec_port_addr;
157	u8 reg;
158
159	mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK;
160	mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK;
161
162	mutex_lock(&mec_io_mutex);
163	outb(mec_port_addr, MEC_ADDR);
164	reg = inb(MEC_DATA_OFFSET(mec_data_addr));
165	mutex_unlock(&mec_io_mutex);
166
167	return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01;
168}
169
170static enum led_brightness silicom_mec_led_get(int channel)
171{
172	/* Outputs are active low */
173	return silicom_mec_port_get(channel) ? LED_OFF : LED_ON;
174}
175
176static void silicom_mec_port_set(int channel, int on)
177{
178
179	unsigned short mec_data_addr;
180	unsigned short mec_port_addr;
181	u8 reg;
182
183	mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK;
184	mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK;
185
186	mutex_lock(&mec_io_mutex);
187	outb(mec_port_addr, MEC_ADDR);
188	reg = inb(MEC_DATA_OFFSET(mec_data_addr));
189	/* Outputs are active low, so clear the bit for on, or set it for off */
190	if (on)
191		reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK));
192	else
193		reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK);
194	outb(reg, MEC_DATA_OFFSET(mec_data_addr));
195	mutex_unlock(&mec_io_mutex);
196}
197
198static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev)
199{
200	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
201	enum led_brightness brightness = LED_OFF;
202	int i;
203
204	for (i = 0; i < mc_cdev->num_colors; i++) {
205		mc_cdev->subled_info[i].brightness =
206			silicom_mec_led_get(mc_cdev->subled_info[i].channel);
207		/* Mark the overall brightness as LED_ON if any of the subleds are on */
208		if (mc_cdev->subled_info[i].brightness != LED_OFF)
209			brightness = LED_ON;
210	}
211
212	return brightness;
213}
214
215static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
216					      enum led_brightness brightness)
217{
218	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
219	int i;
220
221	led_mc_calc_color_components(mc_cdev, brightness);
222	for (i = 0; i < mc_cdev->num_colors; i++) {
223		silicom_mec_port_set(mc_cdev->subled_info[i].channel,
224				     mc_cdev->subled_info[i].brightness);
225	}
226}
227
228static int silicom_gpio_get_direction(struct gpio_chip *gc,
229				      unsigned int offset)
230{
231	u8 *channels = gpiochip_get_data(gc);
232
233	/* Input registers have offsets between [0x00, 0x07] */
234	if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS)
235		return GPIO_LINE_DIRECTION_IN;
236
237	return GPIO_LINE_DIRECTION_OUT;
238}
239
240static int silicom_gpio_direction_input(struct gpio_chip *gc,
241					unsigned int offset)
242{
243	int direction = silicom_gpio_get_direction(gc, offset);
244
245	return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL;
246}
247
248static 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
259static 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
273static 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
281static 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
302static 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
323static 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
350static 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
377static 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
404static 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
463static 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
480static 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
495static 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
522static 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
549static 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
576static 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
613static 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 */
625static 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}
644static DEVICE_ATTR_RO(efuse_status);
645
646static 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}
670static DEVICE_ATTR_RO(uc_version);
671
672static 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
679static 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
689static 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}
709static DEVICE_ATTR_RW(power_cycle);
710
711static 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};
717ATTRIBUTE_GROUPS(silicom);
718
719static struct platform_driver silicom_platform_driver = {
720	.driver = {
721		.name = "silicom-platform",
722		.dev_groups = silicom_groups,
723	},
724};
725
726static 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
753static 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
767static 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
781static 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
792static 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
803static 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
814static 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
825static 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
839static 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
854static 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
871static 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
877static const struct hwmon_chip_info silicom_chip_info = {
878	.ops = &silicom_fan_control_hwmon_ops,
879	.info = silicom_fan_control_info,
880};
881
882static 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
928static 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
940static 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};
970MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
971
972static 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
985static void __exit silicom_platform_exit(void)
986{
987	platform_device_unregister(silicom_platform_dev);
988	platform_driver_unregister(&silicom_platform_driver);
989}
990
991module_init(silicom_platform_init);
992module_exit(silicom_platform_exit);
993
994MODULE_LICENSE("GPL");
995MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
996MODULE_DESCRIPTION("Platform driver for Silicom network appliances");
~/ Read-only file preview.