Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
15 changes: 10 additions & 5 deletions drivers/clk/qcom/gcc-sa8775p.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,11 @@ 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(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 +741,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 +755,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 +769,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 +783,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 +797,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