brintos

brintos / linux-shallow public Read only

0
0
Text · 16.6 KiB · d28815e Raw
677 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/phy.h>4#include <linux/module.h>5 6#include <linux/netdevice.h>7#include <linux/etherdevice.h>8#include <linux/ethtool_netlink.h>9 10#include "qcom.h"11 12MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");13MODULE_AUTHOR("Matus Ujhelyi");14MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");15MODULE_LICENSE("GPL");16 17int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)18{19	int ret;20 21	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);22	if (ret < 0)23		return ret;24 25	return phy_read(phydev, AT803X_DEBUG_DATA);26}27EXPORT_SYMBOL_GPL(at803x_debug_reg_read);28 29int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,30			  u16 clear, u16 set)31{32	u16 val;33	int ret;34 35	ret = at803x_debug_reg_read(phydev, reg);36	if (ret < 0)37		return ret;38 39	val = ret & 0xffff;40	val &= ~clear;41	val |= set;42 43	return phy_write(phydev, AT803X_DEBUG_DATA, val);44}45EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);46 47int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)48{49	int ret;50 51	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);52	if (ret < 0)53		return ret;54 55	return phy_write(phydev, AT803X_DEBUG_DATA, data);56}57EXPORT_SYMBOL_GPL(at803x_debug_reg_write);58 59int at803x_set_wol(struct phy_device *phydev,60		   struct ethtool_wolinfo *wol)61{62	int ret, irq_enabled;63 64	if (wol->wolopts & WAKE_MAGIC) {65		struct net_device *ndev = phydev->attached_dev;66		const u8 *mac;67		unsigned int i;68		static const unsigned int offsets[] = {69			AT803X_LOC_MAC_ADDR_32_47_OFFSET,70			AT803X_LOC_MAC_ADDR_16_31_OFFSET,71			AT803X_LOC_MAC_ADDR_0_15_OFFSET,72		};73 74		if (!ndev)75			return -ENODEV;76 77		mac = (const u8 *)ndev->dev_addr;78 79		if (!is_valid_ether_addr(mac))80			return -EINVAL;81 82		for (i = 0; i < 3; i++)83			phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],84				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));85 86		/* Enable WOL interrupt */87		ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);88		if (ret)89			return ret;90	} else {91		/* Disable WOL interrupt */92		ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);93		if (ret)94			return ret;95	}96 97	/* Clear WOL status */98	ret = phy_read(phydev, AT803X_INTR_STATUS);99	if (ret < 0)100		return ret;101 102	/* Check if there are other interrupts except for WOL triggered when PHY is103	 * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can104	 * be passed up to the interrupt PIN.105	 */106	irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);107	if (irq_enabled < 0)108		return irq_enabled;109 110	irq_enabled &= ~AT803X_INTR_ENABLE_WOL;111	if (ret & irq_enabled && !phy_polling_mode(phydev))112		phy_trigger_machine(phydev);113 114	return 0;115}116EXPORT_SYMBOL_GPL(at803x_set_wol);117 118void at803x_get_wol(struct phy_device *phydev,119		    struct ethtool_wolinfo *wol)120{121	int value;122 123	wol->supported = WAKE_MAGIC;124	wol->wolopts = 0;125 126	value = phy_read(phydev, AT803X_INTR_ENABLE);127	if (value < 0)128		return;129 130	if (value & AT803X_INTR_ENABLE_WOL)131		wol->wolopts |= WAKE_MAGIC;132}133EXPORT_SYMBOL_GPL(at803x_get_wol);134 135int at803x_ack_interrupt(struct phy_device *phydev)136{137	int err;138 139	err = phy_read(phydev, AT803X_INTR_STATUS);140 141	return (err < 0) ? err : 0;142}143EXPORT_SYMBOL_GPL(at803x_ack_interrupt);144 145int at803x_config_intr(struct phy_device *phydev)146{147	int err;148	int value;149 150	value = phy_read(phydev, AT803X_INTR_ENABLE);151 152	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {153		/* Clear any pending interrupts */154		err = at803x_ack_interrupt(phydev);155		if (err)156			return err;157 158		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;159		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;160		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;161		value |= AT803X_INTR_ENABLE_LINK_FAIL;162		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;163 164		err = phy_write(phydev, AT803X_INTR_ENABLE, value);165	} else {166		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);167		if (err)168			return err;169 170		/* Clear any pending interrupts */171		err = at803x_ack_interrupt(phydev);172	}173 174	return err;175}176EXPORT_SYMBOL_GPL(at803x_config_intr);177 178irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)179{180	int irq_status, int_enabled;181 182	irq_status = phy_read(phydev, AT803X_INTR_STATUS);183	if (irq_status < 0) {184		phy_error(phydev);185		return IRQ_NONE;186	}187 188	/* Read the current enabled interrupts */189	int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);190	if (int_enabled < 0) {191		phy_error(phydev);192		return IRQ_NONE;193	}194 195	/* See if this was one of our enabled interrupts */196	if (!(irq_status & int_enabled))197		return IRQ_NONE;198 199	phy_trigger_machine(phydev);200 201	return IRQ_HANDLED;202}203EXPORT_SYMBOL_GPL(at803x_handle_interrupt);204 205int at803x_read_specific_status(struct phy_device *phydev,206				struct at803x_ss_mask ss_mask)207{208	int ss;209 210	/* Read the AT8035 PHY-Specific Status register, which indicates the211	 * speed and duplex that the PHY is actually using, irrespective of212	 * whether we are in autoneg mode or not.213	 */214	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);215	if (ss < 0)216		return ss;217 218	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {219		int sfc, speed;220 221		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);222		if (sfc < 0)223			return sfc;224 225		speed = ss & ss_mask.speed_mask;226		speed >>= ss_mask.speed_shift;227 228		switch (speed) {229		case AT803X_SS_SPEED_10:230			phydev->speed = SPEED_10;231			break;232		case AT803X_SS_SPEED_100:233			phydev->speed = SPEED_100;234			break;235		case AT803X_SS_SPEED_1000:236			phydev->speed = SPEED_1000;237			break;238		case QCA808X_SS_SPEED_2500:239			phydev->speed = SPEED_2500;240			break;241		}242		if (ss & AT803X_SS_DUPLEX)243			phydev->duplex = DUPLEX_FULL;244		else245			phydev->duplex = DUPLEX_HALF;246 247		if (ss & AT803X_SS_MDIX)248			phydev->mdix = ETH_TP_MDI_X;249		else250			phydev->mdix = ETH_TP_MDI;251 252		switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {253		case AT803X_SFC_MANUAL_MDI:254			phydev->mdix_ctrl = ETH_TP_MDI;255			break;256		case AT803X_SFC_MANUAL_MDIX:257			phydev->mdix_ctrl = ETH_TP_MDI_X;258			break;259		case AT803X_SFC_AUTOMATIC_CROSSOVER:260			phydev->mdix_ctrl = ETH_TP_MDI_AUTO;261			break;262		}263	}264 265	return 0;266}267EXPORT_SYMBOL_GPL(at803x_read_specific_status);268 269int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)270{271	u16 val;272 273	switch (ctrl) {274	case ETH_TP_MDI:275		val = AT803X_SFC_MANUAL_MDI;276		break;277	case ETH_TP_MDI_X:278		val = AT803X_SFC_MANUAL_MDIX;279		break;280	case ETH_TP_MDI_AUTO:281		val = AT803X_SFC_AUTOMATIC_CROSSOVER;282		break;283	default:284		return 0;285	}286 287	return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,288			  AT803X_SFC_MDI_CROSSOVER_MODE_M,289			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));290}291EXPORT_SYMBOL_GPL(at803x_config_mdix);292 293int at803x_prepare_config_aneg(struct phy_device *phydev)294{295	int ret;296 297	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);298	if (ret < 0)299		return ret;300 301	/* Changes of the midx bits are disruptive to the normal operation;302	 * therefore any changes to these registers must be followed by a303	 * software reset to take effect.304	 */305	if (ret == 1) {306		ret = genphy_soft_reset(phydev);307		if (ret < 0)308			return ret;309	}310 311	return 0;312}313EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);314 315int at803x_read_status(struct phy_device *phydev)316{317	struct at803x_ss_mask ss_mask = { 0 };318	int err, old_link = phydev->link;319 320	/* Update the link, but return if there was an error */321	err = genphy_update_link(phydev);322	if (err)323		return err;324 325	/* why bother the PHY if nothing can have changed */326	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)327		return 0;328 329	phydev->speed = SPEED_UNKNOWN;330	phydev->duplex = DUPLEX_UNKNOWN;331	phydev->pause = 0;332	phydev->asym_pause = 0;333 334	err = genphy_read_lpa(phydev);335	if (err < 0)336		return err;337 338	ss_mask.speed_mask = AT803X_SS_SPEED_MASK;339	ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);340	err = at803x_read_specific_status(phydev, ss_mask);341	if (err < 0)342		return err;343 344	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)345		phy_resolve_aneg_pause(phydev);346 347	return 0;348}349EXPORT_SYMBOL_GPL(at803x_read_status);350 351static int at803x_get_downshift(struct phy_device *phydev, u8 *d)352{353	int val;354 355	val = phy_read(phydev, AT803X_SMART_SPEED);356	if (val < 0)357		return val;358 359	if (val & AT803X_SMART_SPEED_ENABLE)360		*d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;361	else362		*d = DOWNSHIFT_DEV_DISABLE;363 364	return 0;365}366 367static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)368{369	u16 mask, set;370	int ret;371 372	switch (cnt) {373	case DOWNSHIFT_DEV_DEFAULT_COUNT:374		cnt = AT803X_DEFAULT_DOWNSHIFT;375		fallthrough;376	case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:377		set = AT803X_SMART_SPEED_ENABLE |378		      AT803X_SMART_SPEED_BYPASS_TIMER |379		      FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);380		mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;381		break;382	case DOWNSHIFT_DEV_DISABLE:383		set = 0;384		mask = AT803X_SMART_SPEED_ENABLE |385		       AT803X_SMART_SPEED_BYPASS_TIMER;386		break;387	default:388		return -EINVAL;389	}390 391	ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);392 393	/* After changing the smart speed settings, we need to perform a394	 * software reset, use phy_init_hw() to make sure we set the395	 * reapply any values which might got lost during software reset.396	 */397	if (ret == 1)398		ret = phy_init_hw(phydev);399 400	return ret;401}402 403int at803x_get_tunable(struct phy_device *phydev,404		       struct ethtool_tunable *tuna, void *data)405{406	switch (tuna->id) {407	case ETHTOOL_PHY_DOWNSHIFT:408		return at803x_get_downshift(phydev, data);409	default:410		return -EOPNOTSUPP;411	}412}413EXPORT_SYMBOL_GPL(at803x_get_tunable);414 415int at803x_set_tunable(struct phy_device *phydev,416		       struct ethtool_tunable *tuna, const void *data)417{418	switch (tuna->id) {419	case ETHTOOL_PHY_DOWNSHIFT:420		return at803x_set_downshift(phydev, *(const u8 *)data);421	default:422		return -EOPNOTSUPP;423	}424}425EXPORT_SYMBOL_GPL(at803x_set_tunable);426 427int at803x_cdt_fault_length(int dt)428{429	/* According to the datasheet the distance to the fault is430	 * DELTA_TIME * 0.824 meters.431	 *432	 * The author suspect the correct formula is:433	 *434	 *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2435	 *436	 * where c is the speed of light, VF is the velocity factor of437	 * the twisted pair cable, 125MHz the counter frequency and438	 * we need to divide by 2 because the hardware will measure the439	 * round trip time to the fault and back to the PHY.440	 *441	 * With a VF of 0.69 we get the factor 0.824 mentioned in the442	 * datasheet.443	 */444	return (dt * 824) / 10;445}446EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);447 448int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)449{450	return phy_write(phydev, AT803X_CDT, cdt_start);451}452EXPORT_SYMBOL_GPL(at803x_cdt_start);453 454int at803x_cdt_wait_for_completion(struct phy_device *phydev,455				   u32 cdt_en)456{457	int val, ret;458 459	/* One test run takes about 25ms */460	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,461				    !(val & cdt_en),462				    30000, 100000, true);463 464	return ret < 0 ? ret : 0;465}466EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);467 468static bool qca808x_cdt_fault_length_valid(int cdt_code)469{470	switch (cdt_code) {471	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:472	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:473	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:474	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:475	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:476	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:477	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:478	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:479	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:480	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:481	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:482		return true;483	default:484		return false;485	}486}487 488static int qca808x_cable_test_result_trans(int cdt_code)489{490	switch (cdt_code) {491	case QCA808X_CDT_STATUS_STAT_NORMAL:492		return ETHTOOL_A_CABLE_RESULT_CODE_OK;493	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:494		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;495	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:496		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;497	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:498	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:499	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:500	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:501	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:502	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:503	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:504	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:505	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:506		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;507	case QCA808X_CDT_STATUS_STAT_FAIL:508	default:509		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;510	}511}512 513static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,514				    int result)515{516	int val;517	u32 cdt_length_reg = 0;518 519	switch (pair) {520	case ETHTOOL_A_CABLE_PAIR_A:521		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;522		break;523	case ETHTOOL_A_CABLE_PAIR_B:524		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;525		break;526	case ETHTOOL_A_CABLE_PAIR_C:527		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;528		break;529	case ETHTOOL_A_CABLE_PAIR_D:530		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;531		break;532	default:533		return -EINVAL;534	}535 536	val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);537	if (val < 0)538		return val;539 540	if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)541		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);542	else543		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);544 545	return at803x_cdt_fault_length(val);546}547 548static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,549					      u16 status)550{551	int length, result;552	u16 pair_code;553 554	switch (pair) {555	case ETHTOOL_A_CABLE_PAIR_A:556		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);557		break;558	case ETHTOOL_A_CABLE_PAIR_B:559		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);560		break;561	case ETHTOOL_A_CABLE_PAIR_C:562		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);563		break;564	case ETHTOOL_A_CABLE_PAIR_D:565		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);566		break;567	default:568		return -EINVAL;569	}570 571	result = qca808x_cable_test_result_trans(pair_code);572	ethnl_cable_test_result(phydev, pair, result);573 574	if (qca808x_cdt_fault_length_valid(pair_code)) {575		length = qca808x_cdt_fault_length(phydev, pair, result);576		ethnl_cable_test_fault_length(phydev, pair, length);577	}578 579	return 0;580}581 582int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)583{584	int ret, val;585 586	*finished = false;587 588	val = QCA808X_CDT_ENABLE_TEST |589	      QCA808X_CDT_LENGTH_UNIT;590	ret = at803x_cdt_start(phydev, val);591	if (ret)592		return ret;593 594	ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);595	if (ret)596		return ret;597 598	val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);599	if (val < 0)600		return val;601 602	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);603	if (ret)604		return ret;605 606	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);607	if (ret)608		return ret;609 610	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);611	if (ret)612		return ret;613 614	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);615	if (ret)616		return ret;617 618	*finished = true;619 620	return 0;621}622EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);623 624int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)625{626	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,627				  QCA808X_LED_FORCE_EN);628}629EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);630 631bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)632{633	int val;634 635	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);636	return !(val & QCA808X_LED_FORCE_EN);637}638EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);639 640int qca808x_led_reg_brightness_set(struct phy_device *phydev,641				   u16 reg, enum led_brightness value)642{643	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,644			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,645			      QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :646							      QCA808X_LED_FORCE_OFF));647}648EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);649 650int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,651			      unsigned long *delay_on,652			      unsigned long *delay_off)653{654	int ret;655 656	/* Set blink to 50% off, 50% on at 4Hz by default */657	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,658			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,659			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);660	if (ret)661		return ret;662 663	/* We use BLINK_1 for normal blinking */664	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,665			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,666			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);667	if (ret)668		return ret;669 670	/* We set blink to 4Hz, aka 250ms */671	*delay_on = 250 / 2;672	*delay_off = 250 / 2;673 674	return 0;675}676EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);677