brintos

brintos / linux-shallow public Read only

0
0
Text · 24.4 KiB · c0910af Raw
1000 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// silicom-platform.c - Silicom MEC170x platform driver4//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 0x2426#define MEC_EFUSE_LSB_ADDR   0x2827#define MEC_GPIO_IN_POS      0x0828#define MEC_IO_BASE          0x080029#define MEC_IO_LEN           0x830#define IO_REG_BANK          0x031#define DEFAULT_CHAN_LO      032#define DEFAULT_CHAN_HI      033#define DEFAULT_CHAN_LO_T    0xc34#define MEC_ADDR             (MEC_IO_BASE + 0x02)35#define EC_ADDR_LSB          MEC_ADDR36#define SILICOM_MEC_MAGIC    0x5a37 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	NULL63};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	else193		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 sleep478	 * if the lock blocks479	 */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 the491	 * plat_0222 variant, the rest are the same, so use the same longer list,492	 * but ignore the last entries here493	 */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 interface627 */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");1000