Skip to content
1 change: 1 addition & 0 deletions arch/arm64/configs/qcom_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ CONFIG_IIO=y
CONFIG_QCOM_SPMI_VADC=m
CONFIG_QCOM_SPMI_ADC5=y
CONFIG_PWM=y
CONFIG_PWM_CLK=y
CONFIG_PWM_GPIO=m
CONFIG_PWM_PCA9632=m
CONFIG_PWM_PCA9685=m
Expand Down
22 changes: 20 additions & 2 deletions drivers/clk/qcom/clk-branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,27 @@ static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
return (val & CBCR_CLK_OFF) == (invert ? 0 : CBCR_CLK_OFF);
}

static int get_branch_timeout(const struct clk_branch *br)
{
unsigned long rate;
int timeout;

/*
* The time it takes a clock branch to toggle is roughly 3 clock cycles.
*/
rate = clk_hw_get_rate(&br->clkr.hw);
if (!rate)
return 200;

timeout = 3 * (USEC_PER_SEC / rate);

return max(timeout, 200);
}

static int clk_branch_wait(const struct clk_branch *br, bool enabling,
bool (check_halt)(const struct clk_branch *, bool))
{
int timeout, count;
bool voted = br->halt_check & BRANCH_VOTED;
const char *name = clk_hw_get_name(&br->clkr.hw);

Expand All @@ -78,9 +96,9 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling,
} else if (br->halt_check == BRANCH_HALT_ENABLE ||
br->halt_check == BRANCH_HALT ||
(enabling && voted)) {
int count = 200;
timeout = get_branch_timeout(br);

while (count-- > 0) {
for (count = timeout; count > 0; count--) {
if (check_halt(br, enabling))
return 0;
udelay(1);
Expand Down
2 changes: 2 additions & 0 deletions drivers/clk/qcom/clk-rcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ extern const struct clk_ops clk_dyn_rcg_ops;
* @clkr: regmap clock handle
* @cfg_off: defines the cfg register offset from the CMD_RCGR + CFG_REG
* @parked_cfg: cached value of the CFG register for parked RCGs
* @configured_freq: last configured frequency, used for timeout calculation
* @hw_clk_ctrl: whether to enable hardware clock control
*/
struct clk_rcg2 {
Expand All @@ -153,6 +154,7 @@ struct clk_rcg2 {
struct clk_regmap clkr;
u8 cfg_off;
u32 parked_cfg;
unsigned long configured_freq;
bool hw_clk_ctrl;
};

Expand Down
23 changes: 21 additions & 2 deletions drivers/clk/qcom/clk-rcg2.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,24 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw)
return __clk_rcg2_get_parent(hw, cfg);
}

static int get_update_timeout(const struct clk_rcg2 *rcg)
{
int timeout = 0;
unsigned long current_freq;

/* Allow roughly three cycles at both the old and new clock rates. */
current_freq = clk_hw_get_rate(&rcg->clkr.hw);
if (current_freq)
timeout += 3 * (USEC_PER_SEC / current_freq);
if (rcg->configured_freq)
timeout += 3 * (USEC_PER_SEC / rcg->configured_freq);

return max(timeout, 500);
}

static int update_config(struct clk_rcg2 *rcg)
{
int count, ret;
int timeout, count, ret;
u32 cmd;
struct clk_hw *hw = &rcg->clkr.hw;
const char *name = clk_hw_get_name(hw);
Expand All @@ -120,8 +135,10 @@ static int update_config(struct clk_rcg2 *rcg)
if (ret)
return ret;

timeout = get_update_timeout(rcg);

/* Wait for update to take effect */
for (count = 500; count > 0; count--) {
for (count = timeout; count > 0; count--) {
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
if (ret)
return ret;
Expand Down Expand Up @@ -345,6 +362,8 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
if (ret)
return ret;

rcg->configured_freq = f->freq;

return update_config(rcg);
}

Expand Down
17 changes: 12 additions & 5 deletions drivers/clk/qcom/gcc-sa8775p.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,13 @@ static struct clk_rcg2 gcc_emac1_rgmii_clk_src = {
};

static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = {
F(1000, P_BI_TCXO, 16, 1, 1200),
F(5000, P_BI_TCXO, 16, 1, 240),
F(10000, P_BI_TCXO, 16, 1, 120),
F(20000, P_BI_TCXO, 16, 1, 60),
F(25000, P_BI_TCXO, 16, 1, 48),
F(40000, P_BI_TCXO, 16, 1, 30),
F(100000, P_BI_TCXO, 16, 1, 12),
F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0),
F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0),
{ }
Expand All @@ -736,7 +743,7 @@ static struct clk_rcg2 gcc_gp1_clk_src = {
.name = "gcc_gp1_clk_src",
.parent_data = gcc_parent_data_2,
.num_parents = ARRAY_SIZE(gcc_parent_data_2),
.ops = &clk_rcg2_shared_ops,
.ops = &clk_rcg2_ops,
},
};

Expand All @@ -750,7 +757,7 @@ static struct clk_rcg2 gcc_gp2_clk_src = {
.name = "gcc_gp2_clk_src",
.parent_data = gcc_parent_data_2,
.num_parents = ARRAY_SIZE(gcc_parent_data_2),
.ops = &clk_rcg2_shared_ops,
.ops = &clk_rcg2_ops,
},
};

Expand All @@ -764,7 +771,7 @@ static struct clk_rcg2 gcc_gp3_clk_src = {
.name = "gcc_gp3_clk_src",
.parent_data = gcc_parent_data_2,
.num_parents = ARRAY_SIZE(gcc_parent_data_2),
.ops = &clk_rcg2_shared_ops,
.ops = &clk_rcg2_ops,
},
};

Expand All @@ -778,7 +785,7 @@ static struct clk_rcg2 gcc_gp4_clk_src = {
.name = "gcc_gp4_clk_src",
.parent_data = gcc_parent_data_2,
.num_parents = ARRAY_SIZE(gcc_parent_data_2),
.ops = &clk_rcg2_shared_ops,
.ops = &clk_rcg2_ops,
},
};

Expand All @@ -792,7 +799,7 @@ static struct clk_rcg2 gcc_gp5_clk_src = {
.name = "gcc_gp5_clk_src",
.parent_data = gcc_parent_data_2,
.num_parents = ARRAY_SIZE(gcc_parent_data_2),
.ops = &clk_rcg2_shared_ops,
.ops = &clk_rcg2_ops,
},
};

Expand Down
23 changes: 22 additions & 1 deletion drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct qcom_ethqos {
struct phy *serdes_phy;
unsigned int speed;
int serdes_speed;
bool link_speed_valid;
phy_interface_t phy_mode;
struct reset_control *serdes_reset;

Expand Down Expand Up @@ -948,15 +949,27 @@ static void ethqos_xpcs_validate_link_post_reset(struct qcom_ethqos *ethqos)
}
}

static bool ethqos_needs_speed_change_reset(unsigned int old_speed,
unsigned int new_speed)
{
return old_speed != new_speed &&
(old_speed == SPEED_2500 || new_speed == SPEED_2500);
}

static void ethqos_fix_mac_speed(void *priv_n, unsigned int speed, unsigned int mode)
{
struct qcom_ethqos *ethqos = priv_n;
struct net_device *dev = platform_get_drvdata(ethqos->pdev);
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int old_speed = ethqos->speed;
bool reset_needed;

reset_needed = ethqos->link_speed_valid &&
ethqos_needs_speed_change_reset(old_speed, speed);

qcom_ethqos_set_sgmii_loopback(ethqos, false);
ethqos->speed = speed;
ethqos_update_link_clk(ethqos, speed);
ethqos->speed = speed;
ethqos_configure(ethqos);

if (priv->hw->phylink_pcs) {
Expand All @@ -965,6 +978,14 @@ static void ethqos_fix_mac_speed(void *priv_n, unsigned int speed, unsigned int
DUPLEX_FULL);
ethqos_xpcs_validate_link_post_reset(ethqos);
}

ethqos->link_speed_valid = true;

if (reset_needed) {
netdev_info(dev, "reset adapter after %uMbps to %uMbps link speed change\n",
old_speed, speed);
stmmac_request_reset(priv);
}
}

static int qcom_ethqos_serdes_powerup(struct net_device *ndev, void *priv)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/stmicro/stmmac/stmmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt);
int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size);
int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled);
void stmmac_fpe_handshake(struct stmmac_priv *priv, bool enable);
void stmmac_request_reset(struct stmmac_priv *priv);

static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
{
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ static void stmmac_global_err(struct stmmac_priv *priv)
stmmac_service_event_schedule(priv);
}

void stmmac_request_reset(struct stmmac_priv *priv)
{
stmmac_global_err(priv);
}
EXPORT_SYMBOL_GPL(stmmac_request_reset);

/**
* stmmac_clk_csr_set - dynamically set the MDC clock
* @priv: driver private structure
Expand Down
85 changes: 81 additions & 4 deletions drivers/pwm/pwm-clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@
* - Due to the fact that exact behavior depends on the underlying
* clock driver, various limitations are possible.
* - Underlying clock may not be able to give 0% or 100% duty cycle
* (constant off or on), exact behavior will depend on the clock.
* (constant off or on), exact behavior will depend on the clock,
* unless a gpio pinctrl state is supplied.
* - When the PWM is disabled, the clock will be disabled as well,
* line state will depend on the clock.
* line state will depend on the clock, unless a gpio pinctrl
* state is supplied.
* - The clk API doesn't expose the necessary calls to implement
* .get_state().
*
* Optionally, a GPIO descriptor and pinctrl states ("default" and
* "gpio") can be provided. When a constant output level is needed
* (0% duty, 100% duty, or disabled), the driver switches the pin to
* GPIO mode and drives the appropriate level. For normal PWM output
* the pin is switched back to its clock function mux. If no GPIO is
* provided, the driver falls back to the original clock-only behavior.
*/

#include <linux/kernel.h>
Expand All @@ -25,12 +34,18 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pwm.h>

struct pwm_clk_chip {
struct pwm_chip chip;
struct clk *clk;
bool clk_enabled;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_gpio;
struct gpio_desc *gpiod;
};

#define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
Expand All @@ -43,14 +58,38 @@ static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
u32 rate;
u64 period = state->period;
u64 duty_cycle = state->duty_cycle;
bool constant_level = false;
int gpio_value = 0;

if (!state->enabled) {
if (pwm->state.enabled) {
constant_level = true;
gpio_value = 0;
} else if (state->duty_cycle == 0) {
constant_level = true;
gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 1 : 0;
} else if (state->duty_cycle >= state->period) {
constant_level = true;
gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 0 : 1;
}

if (constant_level) {
if (pcchip->gpiod)
gpiod_direction_output(pcchip->gpiod, gpio_value);
if (pcchip->clk_enabled) {
clk_disable(pcchip->clk);
pcchip->clk_enabled = false;
}
if (pcchip->gpiod) {
pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);
gpiod_direction_output(pcchip->gpiod, gpio_value);
}
return 0;
} else if (!pwm->state.enabled) {
}

if (pcchip->gpiod)
pinctrl_select_state(pcchip->pinctrl, pcchip->pins_default);

if (!pcchip->clk_enabled) {
ret = clk_enable(pcchip->clk);
if (ret)
return ret;
Expand Down Expand Up @@ -93,6 +132,44 @@ static int pwm_clk_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
"Failed to get clock\n");

pcchip->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pcchip->pinctrl)) {
ret = PTR_ERR(pcchip->pinctrl);
pcchip->pinctrl = NULL;
if (ret == -EPROBE_DEFER)
return ret;
} else {
pcchip->pins_default = pinctrl_lookup_state(pcchip->pinctrl,
PINCTRL_STATE_DEFAULT);
pcchip->pins_gpio = pinctrl_lookup_state(pcchip->pinctrl, "gpio");
if (IS_ERR(pcchip->pins_default) || IS_ERR(pcchip->pins_gpio))
pcchip->pinctrl = NULL;
}

/*
* Switch to GPIO pinctrl state before requesting the GPIO. The driver
* core has already applied the default state, which muxes the pin to the
* clock function and claims it. Release that claim first so gpiolib can
* request the pin.
*/
if (pcchip->pinctrl)
pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);

pcchip->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS);
if (IS_ERR(pcchip->gpiod))
return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->gpiod),
"Failed to get gpio\n");

/*
* If pinctrl states were found but no GPIO was provided, the pin is stuck
* in GPIO mode from the switch above. Restore the default mux and fall
* back to clock-only operation.
*/
if (pcchip->pinctrl && !pcchip->gpiod) {
pinctrl_select_state(pcchip->pinctrl, pcchip->pins_default);
pcchip->pinctrl = NULL;
}

pcchip->chip.dev = &pdev->dev;
pcchip->chip.ops = &pwm_clk_ops;
pcchip->chip.npwm = 1;
Expand Down
13 changes: 9 additions & 4 deletions drivers/tty/serial/qcom_geni_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,11 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
if (ret)
return ret;
}

if (uport->rs485.flags & SER_RS485_ENABLED)
qcom_geni_set_rts_pin(uport,
!!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND));

enable_irq(uport->irq);

return 0;
Expand Down Expand Up @@ -1601,12 +1606,12 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
static int qcom_geni_rs485_config(struct uart_port *uport,
struct ktermios *termios, struct serial_rs485 *rs485)
{
/* When RS485 is enabled, keep the RTS pin in ACTIVE state
* and revert back to auto flow control mode, i.e. flow control
* managed by the QUP HW once RS485 is disabled.
/* Initialize RTS to its idle state when RS485 is enabled and revert
* to QUP-managed automatic flow control once RS485 is disabled.
*/
if (rs485->flags & SER_RS485_ENABLED)
qcom_geni_set_rts_pin(uport, true);
qcom_geni_set_rts_pin(uport,
!!(rs485->flags & SER_RS485_RTS_AFTER_SEND));
else
writel(0, uport->membase + SE_UART_MANUAL_RFR);

Expand Down