// SPDX-License-Identifier: GPL-2.0+
//
// silicom-platform.c - Silicom MEC170x platform driver
//
// Copyright (C) 2023 Henry Shi <henrys@silicom-usa.com>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/led-class-multicolor.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/units.h>
#include <linux/gpio/driver.h>
#define MEC_POWER_CYCLE_ADDR 0x24
#define MEC_EFUSE_LSB_ADDR 0x28
#define MEC_GPIO_IN_POS 0x08
#define MEC_IO_BASE 0x0800
#define MEC_IO_LEN 0x8
#define IO_REG_BANK 0x0
#define DEFAULT_CHAN_LO 0
#define DEFAULT_CHAN_HI 0
#define DEFAULT_CHAN_LO_T 0xc
#define MEC_ADDR (MEC_IO_BASE + 0x02)
#define EC_ADDR_LSB MEC_ADDR
#define SILICOM_MEC_MAGIC 0x5a
#define MEC_PORT_CHANNEL_MASK GENMASK(2, 0)
#define MEC_PORT_DWORD_OFFSET GENMASK(31, 3)
#define MEC_DATA_OFFSET_MASK GENMASK(1, 0)
#define MEC_PORT_OFFSET_MASK GENMASK(7, 2)
#define MEC_TEMP_LOC GENMASK(31, 16)
#define MEC_VERSION_LOC GENMASK(15, 8)
#define MEC_VERSION_MAJOR GENMASK(15, 14)
#define MEC_VERSION_MINOR GENMASK(13, 8)
#define EC_ADDR_MSB (MEC_IO_BASE + 0x3)
#define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset))
#define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
#define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
static DEFINE_MUTEX(mec_io_mutex);
static unsigned int efuse_status;
static unsigned int mec_uc_version;
static unsigned int power_cycle;
static const struct hwmon_channel_info *silicom_fan_control_info[] = {
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
NULL
};
struct silicom_platform_info {
int io_base;
int io_len;
struct led_classdev_mc *led_info;
struct gpio_chip *gpiochip;
u8 *gpio_channels;
u16 ngpio;
};
static const char * const plat_0222_gpio_names[] = {
"AUTOM0_SFP_TX_FAULT",
"SLOT2_LED_OUT",
"SIM_M2_SLOT2_B_DET",
"SIM_M2_SLOT2_A_DET",
"SLOT1_LED_OUT",
"SIM_M2_SLOT1_B_DET",
"SIM_M2_SLOT1_A_DET",
"SLOT0_LED_OUT",
"WAN_SFP0_RX_LOS",
"WAN_SFP0_PRSNT_N",
"WAN_SFP0_TX_FAULT",
"AUTOM1_SFP_RX_LOS",
"AUTOM1_SFP_PRSNT_N",
"AUTOM1_SFP_TX_FAULT",
"AUTOM0_SFP_RX_LOS",
"AUTOM0_SFP_PRSNT_N",
"WAN_SFP1_RX_LOS",
"WAN_SFP1_PRSNT_N",
"WAN_SFP1_TX_FAULT",
"SIM_M2_SLOT1_MUX_SEL",
"W_DISABLE_M2_SLOT1_N",
"W_DISABLE_MPCIE_SLOT0_N",
"W_DISABLE_M2_SLOT0_N",
"BT_COMMAND_MODE",
"WAN_SFP1_TX_DISABLE",
"WAN_SFP0_TX_DISABLE",
"AUTOM1_SFP_TX_DISABLE",
"AUTOM0_SFP_TX_DISABLE",
"SIM_M2_SLOT2_MUX_SEL",
"W_DISABLE_M2_SLOT2_N",
"RST_CTL_M2_SLOT_1_N",
"RST_CTL_M2_SLOT_2_N",
"PM_USB_PWR_EN_BOT",
"PM_USB_PWR_EN_TOP",
};
static u8 plat_0222_gpio_channels[] = {
OFFSET_BIT_TO_CHANNEL(0x00, 0),
OFFSET_BIT_TO_CHANNEL(0x00, 1),
OFFSET_BIT_TO_CHANNEL(0x00, 2),
OFFSET_BIT_TO_CHANNEL(0x00, 3),
OFFSET_BIT_TO_CHANNEL(0x00, 4),
OFFSET_BIT_TO_CHANNEL(0x00, 5),
OFFSET_BIT_TO_CHANNEL(0x00, 6),
OFFSET_BIT_TO_CHANNEL(0x00, 7),
OFFSET_BIT_TO_CHANNEL(0x01, 0),
OFFSET_BIT_TO_CHANNEL(0x01, 1),
OFFSET_BIT_TO_CHANNEL(0x01, 2),
OFFSET_BIT_TO_CHANNEL(0x01, 3),
OFFSET_BIT_TO_CHANNEL(0x01, 4),
OFFSET_BIT_TO_CHANNEL(0x01, 5),
OFFSET_BIT_TO_CHANNEL(0x01, 6),
OFFSET_BIT_TO_CHANNEL(0x01, 7),
OFFSET_BIT_TO_CHANNEL(0x02, 0),
OFFSET_BIT_TO_CHANNEL(0x02, 1),
OFFSET_BIT_TO_CHANNEL(0x02, 2),
OFFSET_BIT_TO_CHANNEL(0x09, 0),
OFFSET_BIT_TO_CHANNEL(0x09, 1),
OFFSET_BIT_TO_CHANNEL(0x09, 2),
OFFSET_BIT_TO_CHANNEL(0x09, 3),
OFFSET_BIT_TO_CHANNEL(0x0a, 0),
OFFSET_BIT_TO_CHANNEL(0x0a, 1),
OFFSET_BIT_TO_CHANNEL(0x0a, 2),
OFFSET_BIT_TO_CHANNEL(0x0a, 3),
OFFSET_BIT_TO_CHANNEL(0x0a, 4),
OFFSET_BIT_TO_CHANNEL(0x0a, 5),
OFFSET_BIT_TO_CHANNEL(0x0a, 6),
OFFSET_BIT_TO_CHANNEL(0x0b, 0),
OFFSET_BIT_TO_CHANNEL(0x0b, 1),
OFFSET_BIT_TO_CHANNEL(0x0b, 2),
OFFSET_BIT_TO_CHANNEL(0x0b, 3),
};
static struct platform_device *silicom_platform_dev;
static struct led_classdev_mc *silicom_led_info __initdata;
static struct gpio_chip *silicom_gpiochip __initdata;
static u8 *silicom_gpio_channels __initdata;
static int silicom_mec_port_get(unsigned int offset)
{
unsigned short mec_data_addr;
unsigned short mec_port_addr;
u8 reg;
mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK;
mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK;
mutex_lock(&mec_io_mutex);
outb(mec_port_addr, MEC_ADDR);
reg = inb(MEC_DATA_OFFSET(mec_data_addr));
mutex_unlock(&mec_io_mutex);
return