+
+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_device_control_data {
+ struct device *my_dev;
+ int efuse_status;
+ int uc_version;
+ int power_cycle;
+};
+static struct silicom_device_control_data my_dev_ctl;
+
+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 int silicom_gpio_get_direction(struct gpio_chip *gc, unsigned int offset);
+static int silicom_gpio_direction_input(struct gpio_chip *gc, unsigned int offset);
+static int silicom_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value);
+static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset);
+static void silicom_gpio_set(struct gpio_chip *gc, unsigned int offset, int value);
+static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev);
+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 struct gpio_chip silicom_gpio_chip = {
+ .label = "silicom-gpio",
+ .get_direction = silicom_gpio_get_direction,
+ .direction_input = silicom_gpio_direction_input,
+ .direction_output = silicom_gpio_direction_output,
+ .get = silicom_gpio_get,
+ .set = silicom_gpio_set,
+ .base = -1,
+ .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
+ .names = plat_0222_gpio_names,
+ /* We're using a mutex to protect the indirect access, so we can sleep if the lock blocks */
+ .can_sleep = true,
+};
+
+static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_WHITE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7),
+ },
+ {
+ .color_index = LED_COLOR_ID_YELLOW,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6),
+ },
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5),
+ },
+};
+
+static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_WHITE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4),
+ },
+ {
+ .color_index = LED_COLOR_ID_AMBER,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3),
+ },
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2),
+ },
+};
+
+static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7),
+ },
+ {
+ .color_index = LED_COLOR_ID_YELLOW,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6),
+ },
+};
+
+static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3),
+ },
+ {
+ .color_index = LED_COLOR_ID_YELLOW,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2),
+ },
+};
+
+static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1),
+ },
+ {
+ .color_index = LED_COLOR_ID_YELLOW,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0),
+ },
+};
+
+static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = {
+ {
+ .led_cdev = {
+ .name = "multicolor:wan",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info),
+ .subled_info = plat_0222_wan_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:sys",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info),
+ .subled_info = plat_0222_sys_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:stat1",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info),
+ .subled_info = plat_0222_stat1_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:stat2",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info),
+ .subled_info = plat_0222_stat2_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:stat3",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info),
+ .subled_info = plat_0222_stat3_mc_subled_info,
+ },
+ { },
+};
+
+static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = {
+ .io_base = 0x800,
+ .io_len = 8,
+ .led_info = plat_0222_mc_led_info,
+ .gpiochip = &silicom_gpio_chip,
+ .gpio_channels = plat_0222_gpio_channels,
+ /* The original generic cordoba does not have the last 4 outputs of the plat_0222 BB variant,
+ * the rest are the same, so use the same longer list, but ignore the last entries here
+ */
+ .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
+
+};
+
+static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7),
+ },
+ {
+ .color_index = LED_COLOR_ID_AMBER,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4),
+ },
+};
+
+static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3),
+ },
+ {
+ .color_index = LED_COLOR_ID_AMBER,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6),
+ },
+};
+
+static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = {
+ {
+ .color_index = LED_COLOR_ID_RED,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2),
+ },
+ {
+ .color_index = LED_COLOR_ID_GREEN,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1),
+ },
+ {
+ .color_index = LED_COLOR_ID_BLUE,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0),
+ },
+ {
+ .color_index = LED_COLOR_ID_AMBER,
+ .brightness = 1,
+ .intensity = 0,
+ .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5),
+ },
+};
+
+static struct led_classdev_mc cordoba_mc_led_info[] __initdata = {
+ {
+ .led_cdev = {
+ .name = "multicolor:fp_left",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info),
+ .subled_info = cordoba_fp_left_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:fp_center",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info),
+ .subled_info = cordoba_fp_center_mc_subled_info,
+ },
+ {
+ .led_cdev = {
+ .name = "multicolor:fp_right",
+ .brightness = 0,
+ .max_brightness = 1,
+ .brightness_set = silicom_mec_led_mc_brightness_set,
+ .brightness_get = silicom_mec_led_mc_brightness_get,
+ },
+ .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info),
+ .subled_info = cordoba_fp_right_mc_subled_info,
+ },
+ { },
+};
+
+static struct silicom_platform_info silicom_generic_cordoba_info __initdata = {
+ .io_base = 0x800,
+ .io_len = 8,
+ .led_info = cordoba_mc_led_info,
+ .gpiochip = &silicom_gpio_chip,
+ .gpio_channels = plat_0222_gpio_channels,
+ .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
+};
+
+static struct platform_driver silicom_platform_driver = {
+ .driver = {
+ .name = "silicom-platform",
+ },
+};
+
+void lock_io_modules(void)
+{
+ mutex_lock(&mec_io_mutex);
+}
+EXPORT_SYMBOL(lock_io_modules);
+
+void unlock_io_modules(void)
+{
+ mutex_unlock(&mec_io_mutex);
+}
+EXPORT_SYMBOL(unlock_io_modules);
+
+static ssize_t efuse_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 reg;
+ u32 bank = 0;
+ u32 offset = 0x28;
+ u32 byte_pos = 0;
+
+ mutex_lock(&mec_io_mutex);
+ /* Select memory region */
+ outb(bank, EC_ADDR_MSB);
+ outb(offset, EC_ADDR_LSB);
+
+ /* Get current date from the address */
+ reg = inl(MEC_DATA(byte_pos));
+ mutex_unlock(&mec_io_mutex);
+
+ my_dev_ctl.efuse_status = reg & 0x1;
+
+ return sprintf(buf, "%d\n", my_dev_ctl.efuse_status);
+}
+
+static ssize_t uc_version_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u32 reg;
+ u32 bank = 0;
+ u32 offset = 0x0;
+ u32 byte_pos = 0;
+ int uc_version;
+
+ mutex_lock(&mec_io_mutex);
+ outb(bank, EC_ADDR_MSB);
+ outb(offset, EC_ADDR_LSB);
+
+ reg = inl(MEC_DATA(byte_pos));
+ mutex_unlock(&mec_io_mutex);
+
+ uc_version = (reg >> 8) & 0xFF;
+ if (uc_version >= 64 && uc_version < 128) {
+ uc_version = uc_version - 64;
+ if (uc_version < 10)
+ uc_version = 100 + uc_version;
+ else
+ uc_version = 100 + 10 * (uc_version / 10) + uc_version % 10;
+ } else if (uc_version >= 128 && uc_version < 192) {
+ uc_version = uc_version - 128;
+ if (uc_version < 10)
+ uc_version = 200 + uc_version;
+ else
+ uc_version = 200 + 10 * (uc_version / 10) + uc_version % 10;
+ }
+ my_dev_ctl.uc_version = uc_version;
+ return sprintf(buf, "%d\n", my_dev_ctl.uc_version);
+}
+
+static ssize_t power_cycle_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", my_dev_ctl.power_cycle);
+}
+
+static void powercycle_uc(void)
+{
+ u32 bank = 0;
+ u32 offset = 0x24;
+ u32 byte_pos = 0;
+
+ mutex_lock(&mec_io_mutex);
+ /* Select memory region */
+ outb(bank, EC_ADDR_MSB);
+ outb(offset, EC_ADDR_LSB);
+
+ /* Set to 1 for current date from the address */
+ outb(1, MEC_DATA(byte_pos));
+ mutex_unlock(&mec_io_mutex);
+}
+
+static ssize_t power_cycle_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (sscanf(buf, "%du", &my_dev_ctl.power_cycle) != 1) {
+ dev_err(dev, "Failed to read power_cycle\n");
+ return -EINVAL;
+ }
+ if (my_dev_ctl.power_cycle > 0)
+ powercycle_uc();
+
+ return count;
+}
+
+static struct device_attribute my_dev_attr[] = {
+ {
+ .attr = {.name = "efuse_status", .mode = 0644},
+ .show = efuse_status_show,
+ .store = NULL
+ },
+ {
+ .attr = {.name = "uc_version", .mode = 0644},
+ .show = uc_version_show,
+ .store = NULL
+ },
+ {
+ .attr = {.name = "power_cycle", .mode = 0644},
+ .show = power_cycle_show,
+ .store = power_cycle_store
+ },
+};
+
+static int silicom_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ u8 *channels = gpiochip_get_data(gc);
+
+ /* Input registers have offsets between [0x00, 0x07] */
+ if (CHANNEL_TO_OFFSET(channels[offset]) < 0x08)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int silicom_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+ int direction = silicom_gpio_get_direction(gc, offset);
+
+ return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL;
+}
+
+static void silicom_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ u8 *channels = gpiochip_get_data(gc);
+ int direction = silicom_gpio_get_direction(gc, offset);
+ int channel = channels[offset];
+ u8 reg;
+
+ if (direction == GPIO_LINE_DIRECTION_IN)
+ return;
+
+ mutex_lock(&mec_io_mutex);
+ /* Get the dword offset from the channel */
+ outb((channel >> 3) & 0xfc, MEC_ADDR);
+
+ /* Get the current register */
+ reg = inb(MEC_DATA((channel >> 3) & 0x03));
+ if (value == 0)
+ reg &= ~(1 << (channel & 0x7));
+ else if (value > 0)
+ reg |= 1 << (channel & 0x7);
+ else
+ pr_err("Invalid GPIO value: %d\n", value);
+ outb(reg, MEC_DATA((channel >> 3) & 0x03));
+ mutex_unlock(&mec_io_mutex);
+}
+
+static int silicom_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ int direction = silicom_gpio_get_direction(gc, offset);
+
+ if (direction == GPIO_LINE_DIRECTION_IN)
+ return -EINVAL;
+
+ silicom_gpio_set(gc, offset, value);
+
+ return 0;
+}
+
+static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ u8 *channels = gpiochip_get_data(gc);
+ int channel = channels[offset];
+ u8 reg;
+
+ mutex_lock(&mec_io_mutex);
+ /* Get the dword offset from the channel */
+ outb((channel >> 3) & 0xfc, MEC_ADDR);
+
+ /* Get the current register */
+ reg = inb(MEC_DATA((channel >> 3) & 0x03));
+ mutex_unlock(&mec_io_mutex);
+
+ return (reg >> (channel & 0x7)) & 0x01;
+}
+
+static int __init silicom_mc_leds_register(struct device *dev,
+ const struct led_classdev_mc *mc_leds)
+{
+ struct led_classdev_mc *led;
+ int i, err;
+
+ for (i = 0; mc_leds[i].led_cdev.name; i++) {
+ /* allocate and copy data from the init constansts */
+ led = devm_kzalloc(dev, sizeof(struct led_classdev_mc), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(led)) {
+ dev_err(dev, "Failed to alloc led_classdev_mc[%d]: %ld\n", i, PTR_ERR(led));
+ return -ENOMEM;
+ }
+ memcpy(led, &mc_leds[i], sizeof(*led));
+
+ led->subled_info = devm_kzalloc(dev, led->num_colors * sizeof(struct mc_subled),
+ GFP_KERNEL);
+ if (IS_ERR_OR_NULL(led->subled_info)) {
+ dev_err(dev, "Failed to alloc subled_info[%d]: %ld\n",
+ i, PTR_ERR(led->subled_info));
+ return -ENOMEM;
+ }
+ memcpy(led->subled_info, mc_leds[i].subled_info,
+ led->num_colors * sizeof(struct mc_subled));
+
+ err = devm_led_classdev_multicolor_register(dev, led);
+ if (err) {
+ dev_err(dev, "Failed to register[%d]: %d\n", i, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void silicom_mec_led_set(int channel, int on)
+{
+ u8 reg;
+
+ mutex_lock(&mec_io_mutex);
+ /* Get the dword offset from the channel */
+ outb((channel >> 3) & 0xfc, MEC_ADDR);
+
+ /* Get the current LED settings */
+ reg = inb(MEC_DATA((channel >> 3) & 0x03));
+
+ /* Outputs are active low, so clear the bit for on, or set it for off */
+ if (on)
+ reg &= ~(1 << (channel & 0x7));
+ else
+ reg |= 1 << (channel & 0x7);
+
+ /* Write back the updated register */
+ outb(reg, MEC_DATA((channel >> 3) & 0x03));
+
+ mutex_unlock(&mec_io_mutex);
+}
+
+static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
+ int i;
+
+ led_mc_calc_color_components(mc_cdev, brightness);
+
+ for (i = 0; i < mc_cdev->num_colors; i++) {
+ silicom_mec_led_set(mc_cdev->subled_info[i].channel,
+ mc_cdev->subled_info[i].brightness);
+ }
+}
+
+static enum led_brightness silicom_mec_led_get(int channel)
+{
+ u8 reg;
+
+ mutex_lock(&mec_io_mutex);
+ /* Get the dword offset of the register for this LED from the channel */
+ outb((channel >> 3) & 0xfc, MEC_ADDR);
+ /* Get the current LED settings */
+ reg = inb(MEC_DATA((channel >> 3) & 0x03));
+ mutex_unlock(&mec_io_mutex);
+
+ /* Outputs are active low */
+ return reg & (1 << (channel & 0x7)) ? LED_OFF : LED_ON;
+}
+
+static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev)
+{
+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
+ enum led_brightness brightness = LED_OFF;
+ int i;
+
+ for (i = 0; i < mc_cdev->num_colors; i++) {
+ mc_cdev->subled_info[i].brightness =
+ silicom_mec_led_get(mc_cdev->subled_info[i].channel);
+
+ /* Mark the overall brightness as LED_ON if any of the subleds are on */
+ if (mc_cdev->subled_info[i].brightness != LED_OFF)
+ brightness = LED_ON;
+ }
+
+ return brightness;
+}
+
+
+static u32 rpm_get(void)
+{
+ u32 reg;
+ u32 bank = 0;
+ u32 offset = 0xc;
+ u32 byte_pos = 0;