From: Carlo Szelinsky Date: Sat, 18 Jul 2026 13:38:39 +0000 (+0200) Subject: kernel: pse-pd: add Hasivo HS104 PoE PSE controller driver X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d3c5d345d214013cf1c25852ddf89f3be2a42156;p=thirdparty%2Fopenwrt.git kernel: pse-pd: add Hasivo HS104 PoE PSE controller driver Direct-I2C driver for the HS104 PSE chip (unlike the MCU-fronted realtek-pse-mcu family). Packaged as kmod-pse-hasivo-hs104. This driver will be submitted upstream once the pending PSE-core patches it builds on (above) have landed. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- diff --git a/package/kernel/linux/modules/pse-pd.mk b/package/kernel/linux/modules/pse-pd.mk index 5190d6ebf97..95294fde0f4 100644 --- a/package/kernel/linux/modules/pse-pd.mk +++ b/package/kernel/linux/modules/pse-pd.mk @@ -143,3 +143,21 @@ define KernelPackage/pse-realtek-mcu-uart/description endef $(eval $(call KernelPackage,pse-realtek-mcu-uart)) + +define KernelPackage/pse-hasivo-hs104 + SUBMENU:=$(PSE_MENU) + TITLE:=Hasivo HS104 PSE controller support + KCONFIG:=CONFIG_PSE_HASIVO_HS104 + DEPENDS:=+kmod-i2c-core + FILES:=$(LINUX_DIR)/drivers/net/pse-pd/hasivo_hs104.ko + AUTOLOAD:=$(call AutoProbe,hasivo_hs104) + $(call AddDepends/pse-pd) +endef + +define KernelPackage/pse-hasivo-hs104/description + Kernel module for the Hasivo HS104 PoE PSE controller chips. + Supports the HS104PTI/HS104PBI single-chip PoE PSE controllers + managing 4 delivery channels for 802.3af/at/bt power over I2C. +endef + +$(eval $(call KernelPackage,pse-hasivo-hs104)) diff --git a/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch b/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch new file mode 100644 index 00000000000..cf6a22c99c5 --- /dev/null +++ b/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch @@ -0,0 +1,561 @@ +From 5550ceafb9bff5f0d362d402b7f274ded474cbf4 Mon Sep 17 00:00:00 2001 +From: Bevan Weiss +Date: Mon, 2 Mar 2026 23:31:43 +0100 +Subject: [PATCH] net: pse-pd: add Hasivo HS104 PoE PSE controller driver + +The HS104PTI/HS104PBI are single-chip PoE PSE controllers that manage 4 +power delivery channels and are accessed directly from the host over I2C. +They are found on Hasivo managed PoE switches (e.g. S1100WP-8GT-SE, which +carries two of them). Unlike the MCU-fronted realtek-pse-mcu family, the +PSE silicon here is driven directly, so it needs its own controller +driver. + +Add the driver, its device-tree binding and the Kconfig/Makefile entries. + +Signed-off-by: Bevan Weiss +Co-developed-by: Carlo Szelinsky +Signed-off-by: Carlo Szelinsky +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/pse-pd/hasivo,hs104.yaml +@@ -0,0 +1,64 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/pse-pd/hasivo,hs104.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Hasivo HS104 Power Sourcing Equipment controller ++ ++maintainers: ++ - Bevan Weiss ++ - Carlo Szelinsky ++ ++description: | ++ The HS104PTI/HS104PBI are single-chip PoE PSE controllers managing 4 ++ delivery channels, allowing them to supply 4 ports of 802.3af/at/bt power. ++ The HS104PTI can have 1x 802.3bt port and 3x 802.3at ports. ++ The HS104PBI can have 4x 802.3bt ports. ++ ++allOf: ++ - $ref: pse-controller.yaml# ++ ++properties: ++ compatible: ++ enum: ++ - hasivo,hs104 ++ - hasivo,hs104pti ++ - hasivo,hs104pbi ++ ++ reg: ++ maxItems: 1 ++ ++ pse-pis: ++ patternProperties: ++ '^pse-pi@[0-3]$': ++ type: object ++ ++unevaluatedProperties: false ++ ++required: ++ - compatible ++ - reg ++ - pse-pis ++ ++examples: ++ - | ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethernet-pse@d { ++ compatible = "hasivo,hs104"; ++ reg = <0x0d>; ++ ++ pse-pis { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pse-pi@0 { ++ reg = <0>; ++ #pse-cells = <0>; ++ }; ++ }; ++ }; ++ }; +--- a/drivers/net/pse-pd/Kconfig ++++ b/drivers/net/pse-pd/Kconfig +@@ -80,4 +80,12 @@ config PSE_TPS23881 + + To compile this driver as a module, choose M here: the + module will be called tps23881. ++ ++config PSE_HASIVO_HS104 ++ tristate "Hasivo HS104 PoE PSE Controller" ++ depends on I2C ++ help ++ Support for the Hasivo HS104 PoE PSE Controller chip. ++ This driver allows control and monitoring of PoE ports via I2C. ++ + endif +--- a/drivers/net/pse-pd/Makefile ++++ b/drivers/net/pse-pd/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_PSE_REGULATOR) += pse_regul + obj-$(CONFIG_PSE_PD692X0) += pd692x0.o + obj-$(CONFIG_PSE_SI3474) += si3474.o + obj-$(CONFIG_PSE_TPS23881) += tps23881.o ++obj-$(CONFIG_PSE_HASIVO_HS104) += hasivo_hs104.o +--- /dev/null ++++ b/drivers/net/pse-pd/hasivo_hs104.c +@@ -0,0 +1,451 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Hasivo HS104 PoE PSE controller driver ++ * ++ * The HS104PTI/HS104PBI are single-chip PoE PSE controllers managing 4 ++ * delivery channels, allowing them to supply 4 ports of 802.3af/at/bt power. ++ * The HS104PTI can have 1x 802.3bt port and 3x 802.3at ports. ++ * The HS104PBI can have 4x 802.3bt ports. ++ * ++ * Copyright (c) 2025 Bevan Weiss ++ * Copyright (c) 2026 Carlo Szelinsky ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define HS104_MAX_PORTS 4 ++ ++/* Register map */ ++#define HS104_REG_PW_STATUS 0x01 /* Power delivery status */ ++#define HS104_REG_INPUT_V 0x02 /* Input voltage, 16-bit BE, 10mV */ ++#define HS104_REG_PORT0_I 0x04 /* Port current, 16-bit BE, 1mA (not yet implemented) */ ++#define HS104_REG_DEVID 0x0C /* Device ID */ ++#define HS104_DEVICE_ID 0x91 ++#define HS104_REG_PORT0_CLASS 0x0D /* Port power class */ ++#define HS104_REG_PW_EN 0x14 /* Port enable control */ ++#define HS104_EXECUTE 0x40 /* Execute bit for writes */ ++#define HS104_REG_PROTOCOL 0x19 /* Protocol per port (2 bits each) */ ++#define HS104_PROTO_MASK 0x3 ++#define HS104_PROTO_BT 0 /* 802.3bt - 60W */ ++#define HS104_PROTO_HIPO 1 /* Hi-PoE - 90W */ ++#define HS104_PROTO_AT 2 /* 802.3at - 30W */ ++#define HS104_PROTO_AF 3 /* 802.3af - 15.4W */ ++#define HS104_REG_TOTAL_POWER 0x1D /* Total power, 16-bit BE, 10mW (not yet implemented) */ ++#define HS104_REG_PORT0_POWER 0x21 /* Port power, 16-bit BE, 10mW */ ++ ++/* ++ * BIT-field registers (PW_EN, PW_STATUS) use reversed bit ordering ++ * relative to sequential PORT registers (PORT0_POWER, PORT0_CLASS). ++ * Map PSE PI index to the hardware bit position. ++ */ ++#define HS104_PORT_BIT(id) (0x08 >> (id)) ++ ++/* Power limits in mW */ ++#define HS104_PW_AF 15400 ++#define HS104_PW_AT 30000 ++#define HS104_PW_BT 60000 ++#define HS104_PW_HIPO 90000 ++ ++/* Unit conversion steps */ ++#define HS104_UV_STEP 10000 /* 10mV -> uV */ ++#define HS104_UA_STEP 1000 /* 1mA -> uA */ ++#define HS104_MW_STEP 10 /* 10mW -> mW */ ++ ++struct hs104_priv { ++ struct regmap *regmap; ++ struct pse_controller_dev pcdev; ++ unsigned int last_pw_status; ++}; ++ ++static inline struct hs104_priv *to_hs104(struct pse_controller_dev *pcdev) ++{ ++ return container_of(pcdev, struct hs104_priv, pcdev); ++} ++ ++/* Read 16-bit big-endian register and apply unit conversion */ ++static int hs104_read_be16(struct hs104_priv *priv, unsigned int reg, ++ unsigned int step) ++{ ++ __be16 val; ++ int ret; ++ ++ ret = regmap_bulk_read(priv->regmap, reg, &val, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ /* Hardware uses 14-bit data field, upper 2 bits are reserved */ ++ return (be16_to_cpu(val) & 0x3fff) * step; ++} ++ ++/* Convert protocol code to power limit in mW */ ++static int hs104_proto_to_mw(unsigned int proto) ++{ ++ switch (proto) { ++ case HS104_PROTO_AF: return HS104_PW_AF; ++ case HS104_PROTO_AT: return HS104_PW_AT; ++ case HS104_PROTO_BT: return HS104_PW_BT; ++ case HS104_PROTO_HIPO: return HS104_PW_HIPO; ++ default: return 0; ++ } ++} ++ ++/* PSE controller operations */ ++ ++static int hs104_pi_enable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) ++ return ret; ++ ++ val = (val | HS104_PORT_BIT(id)) | HS104_EXECUTE; ++ ++ dev_dbg(pcdev->dev, "PI %d: enable (0x%02x)\n", id, val); ++ return regmap_write(priv->regmap, HS104_REG_PW_EN, val); ++} ++ ++static int hs104_pi_disable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) ++ return ret; ++ ++ val = (val & ~HS104_PORT_BIT(id)) | HS104_EXECUTE; ++ ++ dev_dbg(pcdev->dev, "PI %d: disable (0x%02x)\n", id, val); ++ return regmap_write(priv->regmap, HS104_REG_PW_EN, val); ++} ++ ++static int hs104_pi_get_voltage(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ ++ /* Input voltage is shared across all PIs */ ++ return hs104_read_be16(priv, HS104_REG_INPUT_V, HS104_UV_STEP); ++} ++ ++static int hs104_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val, proto; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PROTOCOL, &val); ++ if (ret) ++ return ret; ++ ++ proto = (val >> (id * 2)) & HS104_PROTO_MASK; ++ return hs104_proto_to_mw(proto) ?: -ENODATA; ++} ++ ++static int hs104_pi_set_pw_limit(struct pse_controller_dev *pcdev, ++ int id, int max_mw) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int proto, mask; ++ ++ if (max_mw <= HS104_PW_AF) ++ proto = HS104_PROTO_AF; ++ else if (max_mw <= HS104_PW_AT) ++ proto = HS104_PROTO_AT; ++ else if (max_mw <= HS104_PW_BT) ++ proto = HS104_PROTO_BT; ++ else if (max_mw <= HS104_PW_HIPO) ++ proto = HS104_PROTO_HIPO; ++ else ++ return -EINVAL; ++ ++ mask = HS104_PROTO_MASK << (id * 2); ++ proto <<= (id * 2); ++ ++ return regmap_update_bits(priv->regmap, HS104_REG_PROTOCOL, mask, proto); ++} ++ ++static int hs104_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) { ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN; ++ return ret; ++ } ++ ++ if (val & HS104_PORT_BIT(id)) ++ admin_state->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ ++ return 0; ++} ++ ++static int hs104_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int status, pw_en; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_STATUS, &status); ++ if (ret) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN; ++ return ret; ++ } ++ ++ if (status & HS104_PORT_BIT(id)) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; ++ return 0; ++ } ++ ++ /* ++ * Not delivering: tell an administratively disabled port apart from ++ * one that is enabled but has no valid PD attached yet (searching). ++ */ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &pw_en); ++ if (ret) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN; ++ return ret; ++ } ++ ++ if (pw_en & HS104_PORT_BIT(id)) ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; ++ else ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; ++ ++ return 0; ++} ++ ++static int hs104_pi_get_pw_class(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PORT0_CLASS + id, &val); ++ if (ret) ++ return ret; ++ ++ return val; ++} ++ ++static int hs104_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ ++ return hs104_read_be16(priv, HS104_REG_PORT0_POWER + id * 2, ++ HS104_MW_STEP); ++} ++ ++static const struct ethtool_c33_pse_pw_limit_range hs104_pw_ranges[] = { ++ { .min = HS104_PW_AF, .max = HS104_PW_AF }, ++ { .min = HS104_PW_AT, .max = HS104_PW_AT }, ++ { .min = HS104_PW_BT, .max = HS104_PW_BT }, ++ { .min = HS104_PW_HIPO, .max = HS104_PW_HIPO }, ++}; ++ ++static int hs104_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, ++ int id, ++ struct pse_pw_limit_ranges *pw_limit_ranges) ++{ ++ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; ++ ++ c33_pw_limit_ranges = kmemdup(hs104_pw_ranges, sizeof(hs104_pw_ranges), ++ GFP_KERNEL); ++ if (!c33_pw_limit_ranges) ++ return -ENOMEM; ++ ++ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; ++ ++ /* Return number of ranges */ ++ return ARRAY_SIZE(hs104_pw_ranges); ++} ++ ++static int hs104_map_event(int irq, struct pse_controller_dev *pcdev, ++ unsigned long *notifs, unsigned long *notifs_mask) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int pw_status, changed; ++ int ret, i; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_STATUS, &pw_status); ++ if (ret) ++ return ret; ++ ++ /* Report ports whose power-delivery state changed. The HS104 has no ++ * per-fault status, so a delivering transition is mapped to a ++ * detection/disconnection event rather than a bogus over-current. ++ */ ++ changed = pw_status ^ priv->last_pw_status; ++ priv->last_pw_status = pw_status; ++ ++ for (i = 0; i < HS104_MAX_PORTS; i++) { ++ if (!(changed & HS104_PORT_BIT(i))) ++ continue; ++ ++ __set_bit(i, notifs_mask); ++ notifs[i] = (pw_status & HS104_PORT_BIT(i)) ? ++ ETHTOOL_C33_PSE_EVENT_DETECTION : ++ ETHTOOL_C33_PSE_EVENT_DISCONNECTION; ++ } ++ ++ return 0; ++} ++ ++static const struct pse_controller_ops hs104_ops = { ++ .pi_enable = hs104_pi_enable, ++ .pi_disable = hs104_pi_disable, ++ .pi_get_admin_state = hs104_pi_get_admin_state, ++ .pi_get_pw_status = hs104_pi_get_pw_status, ++ .pi_get_pw_class = hs104_pi_get_pw_class, ++ .pi_get_actual_pw = hs104_pi_get_actual_pw, ++ .pi_get_voltage = hs104_pi_get_voltage, ++ .pi_get_pw_limit = hs104_pi_get_pw_limit, ++ .pi_set_pw_limit = hs104_pi_set_pw_limit, ++ .pi_get_pw_limit_ranges = hs104_pi_get_pw_limit_ranges, ++}; ++ ++/* Driver initialization */ ++ ++static const struct regmap_range hs104_rd_ranges[] = { ++ regmap_reg_range(HS104_REG_PW_STATUS, HS104_REG_DEVID), /* 0x01-0x0C */ ++ regmap_reg_range(HS104_REG_PORT0_CLASS, ++ HS104_REG_PORT0_CLASS + HS104_MAX_PORTS - 1), /* 0x0D-0x10 */ ++ regmap_reg_range(HS104_REG_PW_EN, HS104_REG_PW_EN), /* 0x14 */ ++ regmap_reg_range(HS104_REG_PROTOCOL, HS104_REG_PROTOCOL), /* 0x19 */ ++ regmap_reg_range(HS104_REG_TOTAL_POWER, ++ HS104_REG_TOTAL_POWER + 1), /* 0x1D-0x1E */ ++ regmap_reg_range(HS104_REG_PORT0_POWER, ++ HS104_REG_PORT0_POWER + HS104_MAX_PORTS * 2 - 1),/* 0x21-0x28 */ ++}; ++ ++static const struct regmap_range hs104_wr_ranges[] = { ++ regmap_reg_range(HS104_REG_PW_EN, HS104_REG_PW_EN), /* 0x14 */ ++ regmap_reg_range(HS104_REG_PROTOCOL, HS104_REG_PROTOCOL), /* 0x19 */ ++}; ++ ++static const struct regmap_access_table hs104_rd_table = { ++ .yes_ranges = hs104_rd_ranges, ++ .n_yes_ranges = ARRAY_SIZE(hs104_rd_ranges), ++}; ++ ++static const struct regmap_access_table hs104_wr_table = { ++ .yes_ranges = hs104_wr_ranges, ++ .n_yes_ranges = ARRAY_SIZE(hs104_wr_ranges), ++}; ++ ++static const struct regmap_config hs104_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = HS104_REG_PORT0_POWER + HS104_MAX_PORTS * 2 - 1, ++ .rd_table = &hs104_rd_table, ++ .wr_table = &hs104_wr_table, ++}; ++ ++static int hs104_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct hs104_priv *priv; ++ unsigned int devid; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return -ENXIO; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->regmap = devm_regmap_init_i2c(client, &hs104_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ /* Verify device ID */ ++ ret = regmap_read(priv->regmap, HS104_REG_DEVID, &devid); ++ if (ret) ++ return ret; ++ ++ if (devid != HS104_DEVICE_ID) { ++ dev_err(dev, "Unknown device ID: 0x%02x\n", devid); ++ return -ENODEV; ++ } ++ ++ /* ++ * Enable all ports before registering the PSE controller, so that ++ * pse_pi_is_hw_enabled() sees the correct state when consumers ++ * request PSE controls. HS104 only delivers power to valid PDs. ++ */ ++ ret = regmap_write(priv->regmap, HS104_REG_PW_EN, ++ HS104_EXECUTE | GENMASK(HS104_MAX_PORTS - 1, 0)); ++ if (ret) ++ dev_warn(dev, "Failed to enable ports: %d\n", ret); ++ ++ /* Register PSE controller */ ++ priv->pcdev.ops = &hs104_ops; ++ priv->pcdev.dev = dev; ++ priv->pcdev.owner = THIS_MODULE; ++ priv->pcdev.nr_lines = HS104_MAX_PORTS; ++ priv->pcdev.types = ETHTOOL_PSE_C33; ++ ++ /* ++ * Set up the poll helper before registering the controller. Since the ++ * upstream v6 poll path, pse_controller_register() arms the poll work ++ * as its last step, gated on pcdev->polling (which this helper sets). ++ * Registering first would leave the work unarmed and polling - and thus ++ * the LED-trigger state updates - would never start. ++ */ ++ { ++ static const struct pse_irq_desc poll_desc = { ++ .name = "hs104-poll", ++ .map_event = hs104_map_event, ++ }; ++ ++ ret = devm_pse_poll_helper(&priv->pcdev, &poll_desc); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register poll helper\n"); ++ } ++ ++ ret = devm_pse_controller_register(dev, &priv->pcdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register PSE controller\n"); ++ ++ dev_info(dev, "HS104 PSE controller initialized\n"); ++ return 0; ++} ++ ++static const struct of_device_id hs104_of_match[] = { ++ { .compatible = "hasivo,hs104" }, ++ { .compatible = "hasivo,hs104pti" }, ++ { .compatible = "hasivo,hs104pbi" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hs104_of_match); ++ ++static struct i2c_driver hs104_driver = { ++ .driver = { ++ .name = "hasivo-hs104", ++ .of_match_table = hs104_of_match, ++ }, ++ .probe = hs104_probe, ++}; ++module_i2c_driver(hs104_driver); ++ ++MODULE_AUTHOR("Bevan Weiss "); ++MODULE_AUTHOR("Carlo Szelinsky "); ++MODULE_DESCRIPTION("Hasivo HS104 PoE PSE Controller"); ++MODULE_LICENSE("GPL");