Linux Audio

Check our new training course

Loading...
v6.8
   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 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
 267static 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
 281static 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
 289static 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
 310static 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
 331static 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
 358static 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
 385static 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
 412static 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
 471static 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
 488static 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
 503static 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
 530static 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
 557static 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
 584static 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
 621static 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 */
 633static 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}
 652static DEVICE_ATTR_RO(efuse_status);
 653
 654static 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}
 678static DEVICE_ATTR_RO(uc_version);
 679
 680static 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
 687static 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
 697static 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}
 717static DEVICE_ATTR_RW(power_cycle);
 718
 719static 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};
 725ATTRIBUTE_GROUPS(silicom);
 726
 727static struct platform_driver silicom_platform_driver = {
 728	.driver = {
 729		.name = "silicom-platform",
 730		.dev_groups = silicom_groups,
 731	},
 732};
 733
 734static 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
 761static 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
 775static 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
 789static 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
 800static 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
 811static 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
 822static 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
 833static 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
 847static 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
 862static 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
 879static 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
 885static const struct hwmon_chip_info silicom_chip_info = {
 886	.ops = &silicom_fan_control_hwmon_ops,
 887	.info = silicom_fan_control_info,
 888};
 889
 890static 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
 936static 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
 948static 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};
 978MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
 979
 980static 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
 993static void __exit silicom_platform_exit(void)
 994{
 995	platform_device_unregister(silicom_platform_dev);
 996	platform_driver_unregister(&silicom_platform_driver);
 997}
 998
 999module_init(silicom_platform_init);
1000module_exit(silicom_platform_exit);
1001
1002MODULE_LICENSE("GPL");
1003MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
1004MODULE_DESCRIPTION("Platform driver for Silicom network appliances");
v6.13.7
  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 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	silicom_mec_port_set(channel, !value);
 
 
 
 
 
260}
261
262static int silicom_gpio_direction_output(struct gpio_chip *gc,
263					 unsigned int offset,
264					 int value)
265{
266	int direction = silicom_gpio_get_direction(gc, offset);
267
268	if (direction == GPIO_LINE_DIRECTION_IN)
269		return -EINVAL;
270
271	silicom_gpio_set(gc, offset, value);
272
273	return 0;
274}
275
276static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset)
277{
278	u8 *channels = gpiochip_get_data(gc);
279	int channel = channels[offset];
280
281	return silicom_mec_port_get(channel);
282}
283
284static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = {
285	{
286		.color_index = LED_COLOR_ID_WHITE,
287		.brightness = 1,
288		.intensity = 0,
289		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7),
290	},
291	{
292		.color_index = LED_COLOR_ID_YELLOW,
293		.brightness = 1,
294		.intensity = 0,
295		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6),
296	},
297	{
298		.color_index = LED_COLOR_ID_RED,
299		.brightness = 1,
300		.intensity = 0,
301		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5),
302	},
303};
304
305static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = {
306	{
307		.color_index = LED_COLOR_ID_WHITE,
308		.brightness = 1,
309		.intensity = 0,
310		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4),
311	},
312	{
313		.color_index = LED_COLOR_ID_AMBER,
314		.brightness = 1,
315		.intensity = 0,
316		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3),
317	},
318	{
319		.color_index = LED_COLOR_ID_RED,
320		.brightness = 1,
321		.intensity = 0,
322		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2),
323	},
324};
325
326static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = {
327	{
328		.color_index = LED_COLOR_ID_RED,
329		.brightness = 1,
330		.intensity = 0,
331		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1),
332	},
333	{
334		.color_index = LED_COLOR_ID_GREEN,
335		.brightness = 1,
336		.intensity = 0,
337		.channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0),
338	},
339	{
340		.color_index = LED_COLOR_ID_BLUE,
341		.brightness = 1,
342		.intensity = 0,
343		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7),
344	},
345	{
346		.color_index = LED_COLOR_ID_YELLOW,
347		.brightness = 1,
348		.intensity = 0,
349		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6),
350	},
351};
352
353static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = {
354	{
355		.color_index = LED_COLOR_ID_RED,
356		.brightness = 1,
357		.intensity = 0,
358		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5),
359	},
360	{
361		.color_index = LED_COLOR_ID_GREEN,
362		.brightness = 1,
363		.intensity = 0,
364		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4),
365	},
366	{
367		.color_index = LED_COLOR_ID_BLUE,
368		.brightness = 1,
369		.intensity = 0,
370		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3),
371	},
372	{
373		.color_index = LED_COLOR_ID_YELLOW,
374		.brightness = 1,
375		.intensity = 0,
376		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2),
377	},
378};
379
380static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = {
381	{
382		.color_index = LED_COLOR_ID_RED,
383		.brightness = 1,
384		.intensity = 0,
385		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1),
386	},
387	{
388		.color_index = LED_COLOR_ID_GREEN,
389		.brightness = 1,
390		.intensity = 0,
391		.channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0),
392	},
393	{
394		.color_index = LED_COLOR_ID_BLUE,
395		.brightness = 1,
396		.intensity = 0,
397		.channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1),
398	},
399	{
400		.color_index = LED_COLOR_ID_YELLOW,
401		.brightness = 1,
402		.intensity = 0,
403		.channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0),
404	},
405};
406
407static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = {
408	{
409		.led_cdev = {
410			.name = "platled::wan",
411			.brightness = 0,
412			.max_brightness = 1,
413			.brightness_set = silicom_mec_led_mc_brightness_set,
414			.brightness_get = silicom_mec_led_mc_brightness_get,
415		},
416		.num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info),
417		.subled_info = plat_0222_wan_mc_subled_info,
418	},
419	{
420		.led_cdev = {
421			.name = "platled::sys",
422			.brightness = 0,
423			.max_brightness = 1,
424			.brightness_set = silicom_mec_led_mc_brightness_set,
425			.brightness_get = silicom_mec_led_mc_brightness_get,
426		},
427		.num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info),
428		.subled_info = plat_0222_sys_mc_subled_info,
429	},
430	{
431		.led_cdev = {
432			.name = "platled::stat1",
433			.brightness = 0,
434			.max_brightness = 1,
435			.brightness_set = silicom_mec_led_mc_brightness_set,
436			.brightness_get = silicom_mec_led_mc_brightness_get,
437		},
438		.num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info),
439		.subled_info = plat_0222_stat1_mc_subled_info,
440	},
441	{
442		.led_cdev = {
443			.name = "platled::stat2",
444			.brightness = 0,
445			.max_brightness = 1,
446			.brightness_set = silicom_mec_led_mc_brightness_set,
447			.brightness_get = silicom_mec_led_mc_brightness_get,
448		},
449		.num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info),
450		.subled_info = plat_0222_stat2_mc_subled_info,
451	},
452	{
453		.led_cdev = {
454			.name = "platled::stat3",
455			.brightness = 0,
456			.max_brightness = 1,
457			.brightness_set = silicom_mec_led_mc_brightness_set,
458			.brightness_get = silicom_mec_led_mc_brightness_get,
459		},
460		.num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info),
461		.subled_info = plat_0222_stat3_mc_subled_info,
462	},
463	{ },
464};
465
466static struct gpio_chip silicom_gpio_chip = {
467	.label = "silicom-gpio",
468	.get_direction = silicom_gpio_get_direction,
469	.direction_input = silicom_gpio_direction_input,
470	.direction_output = silicom_gpio_direction_output,
471	.get = silicom_gpio_get,
472	.set = silicom_gpio_set,
473	.base = -1,
474	.ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
475	.names = plat_0222_gpio_names,
476	/*
477	 * We're using a mutex to protect the indirect access, so we can sleep
478	 * if the lock blocks
479	 */
480	.can_sleep = true,
481};
482
483static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = {
484	.io_base = MEC_IO_BASE,
485	.io_len = MEC_IO_LEN,
486	.led_info = plat_0222_mc_led_info,
487	.gpiochip = &silicom_gpio_chip,
488	.gpio_channels = plat_0222_gpio_channels,
489	/*
490	 * The original generic cordoba does not have the last 4 outputs of the
491	 * plat_0222 variant, the rest are the same, so use the same longer list,
492	 * but ignore the last entries here
493	 */
494	.ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
495
496};
497
498static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = {
499	{
500		.color_index = LED_COLOR_ID_RED,
501		.brightness = 1,
502		.intensity = 0,
503		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 6),
504	},
505	{
506		.color_index = LED_COLOR_ID_GREEN,
507		.brightness = 1,
508		.intensity = 0,
509		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 5),
510	},
511	{
512		.color_index = LED_COLOR_ID_BLUE,
513		.brightness = 1,
514		.intensity = 0,
515		.channel = OFFSET_BIT_TO_CHANNEL(0x09, 7),
516	},
517	{
518		.color_index = LED_COLOR_ID_AMBER,
519		.brightness = 1,
520		.intensity = 0,
521		.channel = OFFSET_BIT_TO_CHANNEL(0x09, 4),
522	},
523};
524
525static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = {
526	{
527		.color_index = LED_COLOR_ID_RED,
528		.brightness = 1,
529		.intensity = 0,
530		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 7),
531	},
532	{
533		.color_index = LED_COLOR_ID_GREEN,
534		.brightness = 1,
535		.intensity = 0,
536		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 4),
537	},
538	{
539		.color_index = LED_COLOR_ID_BLUE,
540		.brightness = 1,
541		.intensity = 0,
542		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 3),
543	},
544	{
545		.color_index = LED_COLOR_ID_AMBER,
546		.brightness = 1,
547		.intensity = 0,
548		.channel = OFFSET_BIT_TO_CHANNEL(0x09, 6),
549	},
550};
551
552static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = {
553	{
554		.color_index = LED_COLOR_ID_RED,
555		.brightness = 1,
556		.intensity = 0,
557		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 2),
558	},
559	{
560		.color_index = LED_COLOR_ID_GREEN,
561		.brightness = 1,
562		.intensity = 0,
563		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 1),
564	},
565	{
566		.color_index = LED_COLOR_ID_BLUE,
567		.brightness = 1,
568		.intensity = 0,
569		.channel = OFFSET_BIT_TO_CHANNEL(0x08, 0),
570	},
571	{
572		.color_index = LED_COLOR_ID_AMBER,
573		.brightness = 1,
574		.intensity = 0,
575		.channel = OFFSET_BIT_TO_CHANNEL(0x09, 5),
576	},
577};
578
579static struct led_classdev_mc cordoba_mc_led_info[] __initdata = {
580	{
581		.led_cdev = {
582			.name = "platled::fp_left",
583			.brightness = 0,
584			.max_brightness = 1,
585			.brightness_set = silicom_mec_led_mc_brightness_set,
586			.brightness_get = silicom_mec_led_mc_brightness_get,
587		},
588		.num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info),
589		.subled_info = cordoba_fp_left_mc_subled_info,
590	},
591	{
592		.led_cdev = {
593			.name = "platled::fp_center",
594			.brightness = 0,
595			.max_brightness = 1,
596			.brightness_set = silicom_mec_led_mc_brightness_set,
597			.brightness_get = silicom_mec_led_mc_brightness_get,
598		},
599		.num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info),
600		.subled_info = cordoba_fp_center_mc_subled_info,
601	},
602	{
603		.led_cdev = {
604			.name = "platled::fp_right",
605			.brightness = 0,
606			.max_brightness = 1,
607			.brightness_set = silicom_mec_led_mc_brightness_set,
608			.brightness_get = silicom_mec_led_mc_brightness_get,
609		},
610		.num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info),
611		.subled_info = cordoba_fp_right_mc_subled_info,
612	},
613	{ },
614};
615
616static struct silicom_platform_info silicom_generic_cordoba_info __initdata = {
617	.io_base = MEC_IO_BASE,
618	.io_len = MEC_IO_LEN,
619	.led_info = cordoba_mc_led_info,
620	.gpiochip = &silicom_gpio_chip,
621	.gpio_channels = plat_0222_gpio_channels,
622	.ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
623};
624
625/*
626 * sysfs interface
627 */
628static ssize_t efuse_status_show(struct device *dev,
629				 struct device_attribute *attr,
630				 char *buf)
631{
632	u32 reg;
633
634	mutex_lock(&mec_io_mutex);
635	/* Select memory region */
636	outb(IO_REG_BANK, EC_ADDR_MSB);
637	outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB);
638
639	/* Get current data from the address */
640	reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
641	mutex_unlock(&mec_io_mutex);
642
643	efuse_status = reg & 0x1;
644
645	return sysfs_emit(buf, "%u\n", efuse_status);
646}
647static DEVICE_ATTR_RO(efuse_status);
648
649static ssize_t uc_version_show(struct device *dev,
650			       struct device_attribute *attr,
651			       char *buf)
652{
653	int uc_version;
654	u32 reg;
655
656	mutex_lock(&mec_io_mutex);
657	outb(IO_REG_BANK, EC_ADDR_MSB);
658	outb(DEFAULT_CHAN_LO, EC_ADDR_LSB);
659
660	reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
661	mutex_unlock(&mec_io_mutex);
662	uc_version = FIELD_GET(MEC_VERSION_LOC, reg);
663	if (uc_version >= 192)
664		return -EINVAL;
665
666	uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 +
667		     FIELD_GET(MEC_VERSION_MINOR, reg);
668
669	mec_uc_version = uc_version;
670
671	return sysfs_emit(buf, "%u\n", mec_uc_version);
672}
673static DEVICE_ATTR_RO(uc_version);
674
675static ssize_t power_cycle_show(struct device *dev,
676				struct device_attribute *attr,
677				char *buf)
678{
679	return sysfs_emit(buf, "%u\n", power_cycle);
680}
681
682static void powercycle_uc(void)
683{
684	/* Select memory region */
685	outb(IO_REG_BANK, EC_ADDR_MSB);
686	outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB);
687
688	/* Set to 1 for current data from the address */
689	outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
690}
691
692static ssize_t power_cycle_store(struct device *dev,
693				 struct device_attribute *attr,
694				 const char *buf, size_t count)
695{
696	int rc;
697	unsigned int power_cycle_cmd;
698
699	rc = kstrtou32(buf, 0, &power_cycle_cmd);
700	if (rc)
701		return -EINVAL;
702
703	if (power_cycle_cmd > 0) {
704		mutex_lock(&mec_io_mutex);
705		power_cycle = power_cycle_cmd;
706		powercycle_uc();
707		mutex_unlock(&mec_io_mutex);
708	}
709
710	return count;
711}
712static DEVICE_ATTR_RW(power_cycle);
713
714static struct attribute *silicom_attrs[] = {
715	&dev_attr_efuse_status.attr,
716	&dev_attr_uc_version.attr,
717	&dev_attr_power_cycle.attr,
718	NULL,
719};
720ATTRIBUTE_GROUPS(silicom);
721
722static struct platform_driver silicom_platform_driver = {
723	.driver = {
724		.name = "silicom-platform",
725		.dev_groups = silicom_groups,
726	},
727};
728
729static int __init silicom_mc_leds_register(struct device *dev,
730					   const struct led_classdev_mc *mc_leds)
731{
732	int size = sizeof(struct mc_subled);
733	struct led_classdev_mc *led;
734	int i, err;
735
736	for (i = 0; mc_leds[i].led_cdev.name; i++) {
737
738		led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
739		if (!led)
740			return -ENOMEM;
741		memcpy(led, &mc_leds[i], sizeof(*led));
742
743		led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL);
744		if (!led->subled_info)
745			return -ENOMEM;
746		memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size);
747
748		err = devm_led_classdev_multicolor_register(dev, led);
749		if (err)
750			return err;
751	}
752
753	return 0;
754}
755
756static u32 rpm_get(void)
757{
758	u32 reg;
759
760	mutex_lock(&mec_io_mutex);
761	/* Select memory region */
762	outb(IO_REG_BANK, EC_ADDR_MSB);
763	outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
764	reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
765	mutex_unlock(&mec_io_mutex);
766
767	return reg;
768}
769
770static u32 temp_get(void)
771{
772	u32 reg;
773
774	mutex_lock(&mec_io_mutex);
775	/* Select memory region */
776	outb(IO_REG_BANK, EC_ADDR_MSB);
777	outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
778	reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
779	mutex_unlock(&mec_io_mutex);
780
781	return FIELD_GET(MEC_TEMP_LOC, reg) * 100;
782}
783
784static umode_t silicom_fan_control_fan_is_visible(const u32 attr)
785{
786	switch (attr) {
787	case hwmon_fan_input:
788	case hwmon_fan_label:
789		return 0444;
790	default:
791		return 0;
792	}
793}
794
795static umode_t silicom_fan_control_temp_is_visible(const u32 attr)
796{
797	switch (attr) {
798	case hwmon_temp_input:
799	case hwmon_temp_label:
800		return 0444;
801	default:
802		return 0;
803	}
804}
805
806static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val)
807{
808	switch (attr) {
809	case hwmon_fan_input:
810		*val = rpm_get();
811		return 0;
812	default:
813		return -EOPNOTSUPP;
814	}
815}
816
817static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val)
818{
819	switch (attr) {
820	case hwmon_temp_input:
821		*val = temp_get();
822		return 0;
823	default:
824		return -EOPNOTSUPP;
825	}
826}
827
828static umode_t silicom_fan_control_is_visible(const void *data,
829					      enum hwmon_sensor_types type,
830					      u32 attr, int channel)
831{
832	switch (type) {
833	case hwmon_fan:
834		return silicom_fan_control_fan_is_visible(attr);
835	case hwmon_temp:
836		return silicom_fan_control_temp_is_visible(attr);
837	default:
838		return 0;
839	}
840}
841
842static int silicom_fan_control_read(struct device *dev,
843				    enum hwmon_sensor_types type,
844				    u32 attr, int channel,
845				    long *val)
846{
847	switch (type) {
848	case hwmon_fan:
849		return silicom_fan_control_read_fan(dev, attr, val);
850	case hwmon_temp:
851		return silicom_fan_control_read_temp(dev, attr, val);
852	default:
853		return -EOPNOTSUPP;
854	}
855}
856
857static int silicom_fan_control_read_labels(struct device *dev,
858					   enum hwmon_sensor_types type,
859					   u32 attr, int channel,
860					   const char **str)
861{
862	switch (type) {
863	case hwmon_fan:
864		*str = "Silicom_platform: Fan Speed";
865		return 0;
866	case hwmon_temp:
867		*str = "Silicom_platform: Thermostat Sensor";
868		return 0;
869	default:
870		return -EOPNOTSUPP;
871	}
872}
873
874static const struct hwmon_ops silicom_fan_control_hwmon_ops = {
875	.is_visible = silicom_fan_control_is_visible,
876	.read = silicom_fan_control_read,
877	.read_string = silicom_fan_control_read_labels,
878};
879
880static const struct hwmon_chip_info silicom_chip_info = {
881	.ops = &silicom_fan_control_hwmon_ops,
882	.info = silicom_fan_control_info,
883};
884
885static int __init silicom_platform_probe(struct platform_device *device)
886{
887	struct device *hwmon_dev;
888	u8 magic, ver;
889	int err;
890
891	if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) {
892		dev_err(&device->dev, "couldn't reserve MEC io ports\n");
893		return -EBUSY;
894	}
895
896	/* Sanity check magic number read for EC */
897	outb(IO_REG_BANK, MEC_ADDR);
898	magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
899	ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI));
900	dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver);
901
902	if (magic != SILICOM_MEC_MAGIC) {
903		dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic);
904		return -ENODEV;
905	}
906
907	err = silicom_mc_leds_register(&device->dev, silicom_led_info);
908	if (err) {
909		dev_err(&device->dev, "Failed to register LEDs\n");
910		return err;
911	}
912
913	err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip,
914				     silicom_gpio_channels);
915	if (err) {
916		dev_err(&device->dev, "Failed to register gpiochip: %d\n", err);
917		return err;
918	}
919
920	hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL,
921							 &silicom_chip_info, NULL);
922	err = PTR_ERR_OR_ZERO(hwmon_dev);
923	if (err) {
924		dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err);
925		return err;
926	}
927
928	return err;
929}
930
931static int __init silicom_platform_info_init(const struct dmi_system_id *id)
932{
933	struct silicom_platform_info *info = id->driver_data;
934
935	silicom_led_info = info->led_info;
936	silicom_gpio_channels = info->gpio_channels;
937	silicom_gpiochip = info->gpiochip;
938	silicom_gpiochip->ngpio = info->ngpio;
939
940	return 1;
941}
942
943static const struct dmi_system_id silicom_dmi_ids[] __initconst = {
944	{
945		.callback = silicom_platform_info_init,
946		.ident = "Silicom Cordoba (Generic)",
947		.matches = {
948			DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
949			DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"),
950		},
951		.driver_data = &silicom_generic_cordoba_info,
952	},
953	{
954		.callback = silicom_platform_info_init,
955		.ident = "Silicom Cordoba (Generic)",
956		.matches = {
957			DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
958			DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"),
959		},
960		.driver_data = &silicom_generic_cordoba_info,
961	},
962	{
963		 .callback = silicom_platform_info_init,
964		 .ident = "Silicom Cordoba (plat_0222)",
965		 .matches = {
966			DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
967			DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"),
968		 },
969		.driver_data = &silicom_plat_0222_cordoba_info,
970	},
971	{ },
972};
973MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
974
975static int __init silicom_platform_init(void)
976{
977	if (!dmi_check_system(silicom_dmi_ids)) {
978		pr_err("No DMI match for this platform\n");
979		return -ENODEV;
980	}
981	silicom_platform_dev = platform_create_bundle(&silicom_platform_driver,
982						      silicom_platform_probe,
983						      NULL, 0, NULL, 0);
984
985	return PTR_ERR_OR_ZERO(silicom_platform_dev);
986}
987
988static void __exit silicom_platform_exit(void)
989{
990	platform_device_unregister(silicom_platform_dev);
991	platform_driver_unregister(&silicom_platform_driver);
992}
993
994module_init(silicom_platform_init);
995module_exit(silicom_platform_exit);
996
997MODULE_LICENSE("GPL");
998MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
999MODULE_DESCRIPTION("Platform driver for Silicom network appliances");