mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-27 12:57:53 +00:00
Merge branch 'net-phy-generic-polarity-led-support-for-qca808x'
Christian Marangi says: ==================== net: phy: generic polarity + LED support for qca808x This small series add LEDs support for qca808x. QCA808x apply on PHY reset a strange polarity settings and require some tweak to apply a more common configuration found on devices. On adding support for it, it was pointed out that a similar feature is also being implemented for a marvell PHY where LED polarity is set per LED (and not global) and also have a special mode where the LED is tristated. The first 3 patch are to generalize this as we expect more PHY in the future to have a similar configuration. The implementation is extensible to support additional special mode in the future with minimal changes and don't create regression on already implemented PHY drivers. ==================== Link: https://lore.kernel.org/r/20240125203702.4552-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c09f32a859
10 changed files with 431 additions and 19 deletions
|
@ -200,6 +200,18 @@ properties:
|
|||
#trigger-source-cells property in the source node.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
active-low:
|
||||
type: boolean
|
||||
description:
|
||||
Makes LED active low. To turn the LED ON, line needs to be
|
||||
set to low voltage instead of high.
|
||||
|
||||
inactive-high-impedance:
|
||||
type: boolean
|
||||
description:
|
||||
Set LED to high-impedance mode to turn the LED OFF. LED might also
|
||||
describe this mode as tristate.
|
||||
|
||||
# Required properties for flash LED child nodes:
|
||||
flash-max-microamp:
|
||||
description:
|
||||
|
|
|
@ -52,10 +52,6 @@ patternProperties:
|
|||
maxItems: 1
|
||||
description: LED pin number
|
||||
|
||||
active-low:
|
||||
type: boolean
|
||||
description: Makes LED active low
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
|
|
|
@ -78,10 +78,6 @@ patternProperties:
|
|||
- maximum: 23
|
||||
description: LED pin number (only LEDs 0 to 23 are valid).
|
||||
|
||||
active-low:
|
||||
type: boolean
|
||||
description: Makes LED active low.
|
||||
|
||||
brcm,hardware-controlled:
|
||||
type: boolean
|
||||
description: Makes this LED hardware controlled.
|
||||
|
|
|
@ -25,8 +25,6 @@ LED sub-node required properties:
|
|||
|
||||
LED sub-node optional properties:
|
||||
- label : see Documentation/devicetree/bindings/leds/common.txt
|
||||
- active-low : Boolean, makes LED active low.
|
||||
Default : false
|
||||
- default-state : see
|
||||
Documentation/devicetree/bindings/leds/common.txt
|
||||
- linux,default-trigger : see
|
||||
|
|
|
@ -41,10 +41,6 @@ properties:
|
|||
|
||||
pwm-names: true
|
||||
|
||||
active-low:
|
||||
description: For PWMs where the LED is wired to supply rather than ground.
|
||||
type: boolean
|
||||
|
||||
color: true
|
||||
|
||||
required:
|
||||
|
|
|
@ -34,11 +34,6 @@ patternProperties:
|
|||
Maximum brightness possible for the LED
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
active-low:
|
||||
description:
|
||||
For PWMs where the LED is wired to supply rather than ground.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- pwms
|
||||
- max-brightness
|
||||
|
|
54
Documentation/devicetree/bindings/net/qca,qca808x.yaml
Normal file
54
Documentation/devicetree/bindings/net/qca,qca808x.yaml
Normal file
|
@ -0,0 +1,54 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/qca,qca808x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Atheros QCA808X PHY
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
description:
|
||||
QCA808X PHYs can have up to 3 LEDs attached.
|
||||
All 3 LEDs are disabled by default.
|
||||
2 LEDs have dedicated pins with the 3rd LED having the
|
||||
double function of Interrupt LEDs/GPIO or additional LED.
|
||||
|
||||
By default this special PIN is set to LED function.
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-phy.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ethernet-phy-id004d.d101
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet-phy@0 {
|
||||
compatible = "ethernet-phy-id004d.d101";
|
||||
reg = <0>;
|
||||
|
||||
leds {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led@0 {
|
||||
reg = <0>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_WAN;
|
||||
default-state = "keep";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -301,6 +301,87 @@
|
|||
/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
|
||||
#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
|
||||
#define QCA808X_MMD7_LED2_CTRL 0x8074
|
||||
#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
|
||||
#define QCA808X_MMD7_LED1_CTRL 0x8076
|
||||
#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077
|
||||
#define QCA808X_MMD7_LED0_CTRL 0x8078
|
||||
#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
|
||||
|
||||
/* LED hw control pattern is the same for every LED */
|
||||
#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
|
||||
#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
|
||||
#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
|
||||
|
||||
/* LED force ctrl is the same for every LED
|
||||
* No documentation exist for this, not even internal one
|
||||
* with NDA as QCOM gives only info about configuring
|
||||
* hw control pattern rules and doesn't indicate any way
|
||||
* to force the LED to specific mode.
|
||||
* These define comes from reverse and testing and maybe
|
||||
* lack of some info or some info are not entirely correct.
|
||||
* For the basic LED control and hw control these finding
|
||||
* are enough to support LED control in all the required APIs.
|
||||
*
|
||||
* On doing some comparison with implementation with qca807x,
|
||||
* it was found that it's 1:1 equal to it and confirms all the
|
||||
* reverse done. It was also found further specification with the
|
||||
* force mode and the blink modes.
|
||||
*/
|
||||
#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
|
||||
#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
|
||||
/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
|
||||
* LED to active high. It's not clear what BIT 3 and BIT 4 does.
|
||||
*/
|
||||
#define QCA808X_LED_ACTIVE_HIGH BIT(6)
|
||||
|
||||
/* QCA808X 1G chip type */
|
||||
#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
|
@ -346,6 +427,7 @@ struct at803x_priv {
|
|||
struct regulator_dev *vddio_rdev;
|
||||
struct regulator_dev *vddh_rdev;
|
||||
u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
int led_polarity_mode;
|
||||
};
|
||||
|
||||
struct at803x_context {
|
||||
|
@ -706,6 +788,9 @@ static int at803x_probe(struct phy_device *phydev)
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Init LED polarity mode to -1 */
|
||||
priv->led_polarity_mode = -1;
|
||||
|
||||
phydev->priv = priv;
|
||||
|
||||
ret = at803x_parse_dt(phydev);
|
||||
|
@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(struct phy_device *phydev)
|
|||
phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
}
|
||||
|
||||
static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
|
||||
u16 *offload_trigger)
|
||||
{
|
||||
/* Parsing specific to netdev trigger */
|
||||
if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
*offload_trigger |= QCA808X_LED_TX_BLINK;
|
||||
if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
*offload_trigger |= QCA808X_LED_RX_BLINK;
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
|
||||
*offload_trigger |= QCA808X_LED_SPEED10_ON;
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
*offload_trigger |= QCA808X_LED_SPEED100_ON;
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
*offload_trigger |= QCA808X_LED_SPEED1000_ON;
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
|
||||
*offload_trigger |= QCA808X_LED_SPEED2500_ON;
|
||||
if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
*offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
|
||||
if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
*offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
|
||||
|
||||
if (rules && !*offload_trigger)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Enable BLINK_CHECK_BYPASS by default to make the LED
|
||||
* blink even with duplex or speed mode not enabled.
|
||||
*/
|
||||
*offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
|
||||
return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_FORCE_EN);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
unsigned long rules)
|
||||
{
|
||||
u16 offload_trigger = 0;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
|
||||
unsigned long rules)
|
||||
{
|
||||
u16 reg, offload_trigger = 0;
|
||||
int ret;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
|
||||
ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qca808x_led_hw_control_enable(phydev, index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_PATTERN_MASK,
|
||||
offload_trigger);
|
||||
}
|
||||
|
||||
static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
{
|
||||
u16 reg;
|
||||
int val;
|
||||
|
||||
if (index > 2)
|
||||
return false;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
|
||||
return !(val & QCA808X_LED_FORCE_EN);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
unsigned long *rules)
|
||||
{
|
||||
u16 reg;
|
||||
int val;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if we have hw control enabled */
|
||||
if (qca808x_led_hw_control_status(phydev, index))
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
if (val & QCA808X_LED_TX_BLINK)
|
||||
set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
if (val & QCA808X_LED_RX_BLINK)
|
||||
set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
if (val & QCA808X_LED_SPEED10_ON)
|
||||
set_bit(TRIGGER_NETDEV_LINK_10, rules);
|
||||
if (val & QCA808X_LED_SPEED100_ON)
|
||||
set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
if (val & QCA808X_LED_SPEED1000_ON)
|
||||
set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
if (val & QCA808X_LED_SPEED2500_ON)
|
||||
set_bit(TRIGGER_NETDEV_LINK_2500, rules);
|
||||
if (val & QCA808X_LED_HALF_DUPLEX_ON)
|
||||
set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
if (val & QCA808X_LED_FULL_DUPLEX_ON)
|
||||
set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_CTRL(index);
|
||||
|
||||
return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_PATTERN_MASK);
|
||||
}
|
||||
|
||||
static int qca808x_led_brightness_set(struct phy_device *phydev,
|
||||
u8 index, enum led_brightness value)
|
||||
{
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (!value) {
|
||||
ret = qca808x_led_hw_control_reset(phydev, index);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
|
||||
return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
|
||||
QCA808X_LED_FORCE_OFF);
|
||||
}
|
||||
|
||||
static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off)
|
||||
{
|
||||
int ret;
|
||||
u16 reg;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
|
||||
/* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We use BLINK_1 for normal blinking */
|
||||
ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* We set blink to 4Hz, aka 250ms */
|
||||
*delay_on = 250 / 2;
|
||||
*delay_off = 250 / 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
|
||||
unsigned long modes)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
bool active_low = false;
|
||||
u32 mode;
|
||||
|
||||
for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
|
||||
switch (mode) {
|
||||
case PHY_LED_ACTIVE_LOW:
|
||||
active_low = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* PHY polarity is global and can't be set per LED.
|
||||
* To detect this, check if last requested polarity mode
|
||||
* match the new one.
|
||||
*/
|
||||
if (priv->led_polarity_mode >= 0 &&
|
||||
priv->led_polarity_mode != active_low) {
|
||||
phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Save the last PHY polarity mode */
|
||||
priv->led_polarity_mode = active_low;
|
||||
|
||||
return phy_modify_mmd(phydev, MDIO_MMD_AN,
|
||||
QCA808X_MMD7_LED_POLARITY_CTRL,
|
||||
QCA808X_LED_ACTIVE_HIGH,
|
||||
active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
|
||||
}
|
||||
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
|
@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[] = {
|
|||
.cable_test_start = qca808x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
.link_change_notify = qca808x_link_change_notify,
|
||||
.led_brightness_set = qca808x_led_brightness_set,
|
||||
.led_blink_set = qca808x_led_blink_set,
|
||||
.led_hw_is_supported = qca808x_led_hw_is_supported,
|
||||
.led_hw_control_set = qca808x_led_hw_control_set,
|
||||
.led_hw_control_get = qca808x_led_hw_control_get,
|
||||
.led_polarity_set = qca808x_led_polarity_set,
|
||||
}, };
|
||||
|
||||
module_phy_driver(at803x_driver);
|
||||
|
|
|
@ -3097,6 +3097,7 @@ static int of_phy_led(struct phy_device *phydev,
|
|||
struct device *dev = &phydev->mdio.dev;
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
unsigned long modes = 0;
|
||||
struct phy_led *phyled;
|
||||
u32 index;
|
||||
int err;
|
||||
|
@ -3114,6 +3115,21 @@ static int of_phy_led(struct phy_device *phydev,
|
|||
if (index > U8_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_bool(led, "active-low"))
|
||||
set_bit(PHY_LED_ACTIVE_LOW, &modes);
|
||||
if (of_property_read_bool(led, "inactive-high-impedance"))
|
||||
set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
|
||||
|
||||
if (modes) {
|
||||
/* Return error if asked to set polarity modes but not supported */
|
||||
if (!phydev->drv->led_polarity_set)
|
||||
return -EINVAL;
|
||||
|
||||
err = phydev->drv->led_polarity_set(phydev, index, modes);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
phyled->index = index;
|
||||
if (phydev->drv->led_brightness_set)
|
||||
cdev->brightness_set_blocking = phy_led_set_brightness;
|
||||
|
|
|
@ -852,6 +852,15 @@ struct phy_plca_status {
|
|||
bool pst;
|
||||
};
|
||||
|
||||
/* Modes for PHY LED configuration */
|
||||
enum phy_led_modes {
|
||||
PHY_LED_ACTIVE_LOW = 0,
|
||||
PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1,
|
||||
|
||||
/* keep it last */
|
||||
__PHY_LED_MODES_NUM,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct phy_led: An LED driven by the PHY
|
||||
*
|
||||
|
@ -1145,6 +1154,19 @@ struct phy_driver {
|
|||
int (*led_hw_control_get)(struct phy_device *dev, u8 index,
|
||||
unsigned long *rules);
|
||||
|
||||
/**
|
||||
* @led_polarity_set: Set the LED polarity modes
|
||||
* @dev: PHY device which has the LED
|
||||
* @index: Which LED of the PHY device
|
||||
* @modes: bitmap of LED polarity modes
|
||||
*
|
||||
* Configure LED with all the required polarity modes in @modes
|
||||
* to make it correctly turn ON or OFF.
|
||||
*
|
||||
* Returns 0, or an error code.
|
||||
*/
|
||||
int (*led_polarity_set)(struct phy_device *dev, int index,
|
||||
unsigned long modes);
|
||||
};
|
||||
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
|
||||
struct phy_driver, mdiodrv)
|
||||
|
|
Loading…
Reference in a new issue