phy fixes for 7.3

Couple of driver fixes
  - Atomic context delay in renesas driver
  - TMDS and PLL rate calculation fixes for mediatek driver
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmqwD8UACgkQfBQHDyUj
 g0dzqBAAuGIVwSpUaEVAHD7TKcLqrmLpF4kuGXN4mGB+i8Hg3ivCH7n6/cKONOeA
 LPkdnl7ABRqNYX935oXtRAUuy0CnoIKJKaxrp0ap1x5QtAcyAKWEW+Ej/rHL9JV7
 JHt/YQFcMhcvKE1lGUYdFA9eF2PQf0h3Xw0BcaB83WqAlsoWh9EnEOjDnfkv4rZi
 PMIW5/lXlpkWEfyptomcNHycGbltnLP323IkIscyS6qX7dIfldoN4pinwNKrIvHM
 CNrg8PPYefjFltVZ7q2u/MB8IeDpYGKNlyGC3Kvspsa04o8Qb0TTMb3aZ+AOn3rJ
 Ccq69UwdnMARx+V9Gc9gY5Jp6Q503SPwFGhRTybT9iyjft9yIGkaEZL1miWOWpZZ
 Fsv3xukBZhDVy1jGt6tsyfUVvRh6eu835jYijp4NHBeo1To7/clcKTTAT5o1/upC
 qWeM/FarR3JB/sAHrHKnA/R3Yh9FTpy5lTD6FhXa95SufJmB12rKbjUYNMNG3EOx
 OgYpeBeuy8ZXtkzPO36LXFv6OomQkxjThMAZ/syn1T8HStWULkhj9MxkrH0BRsB0
 SsxIBkzG7tKlwQizJGDugum9uvxFX0104voNhTkn9DPO2oo4LnxGlsonfqJj7XPD
 TJoF9/IWV+3bQCL9fLU0mBvniQf/jzEaGXoGX+qY1YtXm8I5jTE=
 =xn2f
 -----END PGP SIGNATURE-----

Merge tag 'phy-fixes-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy

Pull phy fixes from Vinod Koul:

 - avoid atomic context delay in renesas driver

 - TMDS and PLL rate calculation fixes for mediatek driver

* tag 'phy-fixes-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy:
  phy: mediatek: phy-mtk-hdmi-mt8195: Fix TMDS clk bit ratio setting
  phy: mediatek: phy-mtk-hdmi-mt8195: Fix PLL calc divisor overflow
  phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
This commit is contained in:
Linus Torvalds 2026-09-20 10:43:12 -07:00
commit 60ee24f055
3 changed files with 270 additions and 47 deletions

View File

@ -36,7 +36,7 @@ mtk_phy_tmds_clk_ratio(struct mtk_hdmi_phy *hdmi_phy, bool enable)
* clock bit ratio 1:40, under 3.4Gbps, clock bit ratio 1:10
*/
if (enable)
mtk_phy_update_field(regs + HDMI20_CLK_CFG, REG_TXC_DIV, 3);
mtk_phy_update_field(regs + HDMI20_CLK_CFG, REG_TXC_DIV, VAL_TXC_DIV4);
else
mtk_phy_clear_bits(regs + HDMI20_CLK_CFG, REG_TXC_DIV);
}
@ -290,7 +290,7 @@ static int mtk_hdmi_pll_calc(struct mtk_hdmi_phy *hdmi_phy, struct clk_hw *hw,
posdiv2 = 1;
/* Digital clk divider, max /32 */
digital_div = div_u64(ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk);
digital_div = div64_u64(ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk);
if (!(digital_div <= 32 && digital_div >= 1))
return -EINVAL;

View File

@ -17,6 +17,9 @@
#define HDMI20_CLK_CFG 0x70
#define REG_TXC_DIV GENMASK(31, 30)
#define VAL_TXC_DIV2 1
#define VAL_TXC_DIV4 2
#define VAL_TXC_DIV8 3
#define HDMI_1_CFG_0 0x00
#define RG_HDMITX21_DRV_IBIAS_CLK GENMASK(10, 5)

View File

@ -27,6 +27,7 @@
#include <linux/reset.h>
#include <linux/string.h>
#include <linux/usb/of.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/
@ -106,6 +107,13 @@
/* RZ/G2L specific */
#define USB2_LINECTRL1_USB2_IDMON BIT(0)
/*
* The OTG initialization is expected to finish in 20ms. Choose a large enough
* timeout to avoid waiters exit prematurely the waiting section under heavy
* CPU load.
*/
#define USB2_OTG_INIT_TIMEOUT msecs_to_jiffies(120)
#define NUM_OF_PHYS 4
enum rcar_gen3_phy_index {
PHY_INDEX_BOTH_HC,
@ -138,12 +146,20 @@ struct rcar_gen3_chan {
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
struct regulator *vbus;
struct work_struct work;
wait_queue_head_t otg_init_done;
spinlock_t lock; /* protects access to hardware and driver data structure. */
enum usb_dr_mode dr_mode;
bool extcon_host;
bool is_otg_channel;
bool uses_otg_pins;
bool otg_internal_reg;
/*
* The OTG can be initialized only once and needs to release the spinlock
* and wait for 20 ms due to hardware constraints. If a thread executes
* PHY configuration code while the OTG PHY is waiting for the 20 ms, the
* thread will have to wait for the OTG PHY initialization to complete.
*/
bool otg_initializing;
};
struct rcar_gen3_phy_drv_data {
@ -392,26 +408,58 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
bool is_b_device;
enum phy_mode cur_mode, new_mode;
int retries = NUM_OF_PHYS;
unsigned long flags;
int ret = -EIO;
guard(spinlock_irqsave)(&ch->lock);
spin_lock_irqsave(&ch->lock, flags);
if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
return -EIO;
if (!ch->is_otg_channel)
goto unlock;
if (sysfs_streq(buf, "host"))
while (retries-- && ch->otg_initializing) {
spin_unlock_irqrestore(&ch->lock, flags);
ret = wait_event_timeout(ch->otg_init_done, !ch->otg_initializing,
USB2_OTG_INIT_TIMEOUT);
ret = ret ? 0 : -ETIMEDOUT;
if (ret && !retries)
goto exit;
spin_lock_irqsave(&ch->lock, flags);
}
/* If another thread started a new initialization just return -EBUSY. */
if (ch->otg_initializing) {
ret = -EBUSY;
goto unlock;
} else {
ret = 0;
}
if (!rcar_gen3_is_any_otg_rphy_initialized(ch)) {
ret = -EIO;
goto unlock;
}
if (sysfs_streq(buf, "host")) {
new_mode = PHY_MODE_USB_HOST;
else if (sysfs_streq(buf, "peripheral"))
} else if (sysfs_streq(buf, "peripheral")) {
new_mode = PHY_MODE_USB_DEVICE;
else
return -EINVAL;
} else {
ret = -EINVAL;
goto unlock;
}
/* is_b_device: true is B-Device. false is A-Device. */
is_b_device = rcar_gen3_check_id(ch);
cur_mode = rcar_gen3_get_phy_mode(ch);
/* If current and new mode is the same, this returns the error */
if (cur_mode == new_mode)
return -EINVAL;
if (cur_mode == new_mode) {
ret = -EINVAL;
goto unlock;
}
if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */
if (!is_b_device) /* A-Peripheral */
@ -425,7 +473,10 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
rcar_gen3_init_for_peri(ch);
}
return count;
unlock:
spin_unlock_irqrestore(&ch->lock, flags);
exit:
return ret ?: count;
}
static ssize_t role_show(struct device *dev, struct device_attribute *attr,
@ -441,14 +492,11 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(role);
static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
static void rcar_gen3_init_otg_phase0(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
u32 val;
if (!ch->is_otg_channel || rcar_gen3_is_any_otg_rphy_initialized(ch))
return;
/* Should not use functions of read-modify-write a register */
val = readl(usb2_base + USB2_LINECTRL1);
val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
@ -471,7 +519,11 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
}
}
mdelay(20);
}
static void rcar_gen3_init_otg_phase1(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
writel(0xffffffff, usb2_base + USB2_OBINTSTA);
writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTEN);
@ -502,6 +554,7 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
void __iomem *usb2_base = ch->base;
struct device *dev = ch->dev;
irqreturn_t ret = IRQ_NONE;
unsigned long flags;
u32 status;
pm_runtime_get_noresume(dev);
@ -509,33 +562,102 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
if (pm_runtime_suspended(dev))
goto rpm_put;
scoped_guard(spinlock, &ch->lock) {
status = readl(usb2_base + USB2_OBINTSTA);
if (status & ch->phy_data->obint_enable_bits) {
dev_vdbg(dev, "%s: %08x\n", __func__, status);
if (ch->phy_data->vblvl_ctrl)
writel(USB2_OBINTSTA_CLEAR, usb2_base + USB2_OBINTSTA);
else
writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTSTA);
rcar_gen3_device_recognition(ch);
rcar_gen3_configure_vblvl_ctrl(ch);
ret = IRQ_HANDLED;
}
spin_lock_irqsave(&ch->lock, flags);
status = readl(usb2_base + USB2_OBINTSTA);
if (status & ch->phy_data->obint_enable_bits) {
dev_vdbg(dev, "%s: %08x\n", __func__, status);
if (ch->phy_data->vblvl_ctrl)
writel(USB2_OBINTSTA_CLEAR, usb2_base + USB2_OBINTSTA);
else
writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTSTA);
ret = IRQ_HANDLED;
/* This should not happen! */
if (ch->otg_initializing)
goto unlock;
rcar_gen3_device_recognition(ch);
rcar_gen3_configure_vblvl_ctrl(ch);
}
unlock:
spin_unlock_irqrestore(&ch->lock, flags);
rpm_put:
pm_runtime_put_noidle(dev);
return ret;
}
static void rcar_gen3_phy_usb2_irqs_mask_all(struct rcar_gen3_chan *channel,
u32 *masked_irqs_bits)
{
u32 val, bitmask = USB2_INT_ENABLE_UCOM_INTEN;
void __iomem *usb2_base = channel->base;
for (unsigned int i = 0; i < NUM_OF_PHYS; i++)
bitmask |= channel->rphys[i].int_enable_bits;
val = readl(usb2_base + USB2_INT_ENABLE);
*masked_irqs_bits = val & bitmask;
val &= ~bitmask;
writel(val, usb2_base + USB2_INT_ENABLE);
/*
* Don't report channel->phy_data->obint_enable_bits IRQs. These are
* unmasked anyway in rcar_gen3_init_otg_phase1().
*/
val = readl(usb2_base + USB2_OBINTEN);
val &= ~channel->phy_data->obint_enable_bits;
writel(val, usb2_base + USB2_OBINTEN);
}
static void rcar_gen3_phy_usb2_irqs_unmask(struct rcar_gen3_chan *channel,
u32 irqs_bits)
{
u32 val, bitmask = USB2_INT_ENABLE_UCOM_INTEN;
void __iomem *usb2_base = channel->base;
for (unsigned int i = 0; i < NUM_OF_PHYS; i++)
bitmask |= channel->rphys[i].int_enable_bits;
val = readl(usb2_base + USB2_INT_ENABLE);
val &= ~bitmask;
val |= irqs_bits;
writel(val, usb2_base + USB2_INT_ENABLE);
}
static int rcar_gen3_phy_usb2_init(struct phy *p)
{
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
int retries = NUM_OF_PHYS;
unsigned long flags;
u32 val;
int ret;
guard(spinlock_irqsave)(&channel->lock);
spin_lock_irqsave(&channel->lock, flags);
while (retries-- && channel->otg_initializing) {
spin_unlock_irqrestore(&channel->lock, flags);
ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
USB2_OTG_INIT_TIMEOUT);
ret = ret ? 0 : -ETIMEDOUT;
if (ret && !retries)
return ret;
spin_lock_irqsave(&channel->lock, flags);
}
/* If another thread started a new initialization just return -EBUSY. */
if (channel->otg_initializing) {
ret = -EBUSY;
goto unlock;
} else {
ret = 0;
}
/* Initialize USB2 part */
val = readl(usb2_base + USB2_INT_ENABLE);
@ -548,8 +670,23 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
}
/* Initialize otg part (only if we initialize a PHY with IRQs). */
if (rphy->int_enable_bits)
rcar_gen3_init_otg(channel);
if (rphy->int_enable_bits && channel->is_otg_channel &&
!rcar_gen3_is_any_otg_rphy_initialized(channel)) {
u32 masked_irq_bits = 0;
rcar_gen3_init_otg_phase0(channel);
rcar_gen3_phy_usb2_irqs_mask_all(channel, &masked_irq_bits);
channel->otg_initializing = true;
spin_unlock_irqrestore(&channel->lock, flags);
fsleep(20000);
spin_lock_irqsave(&channel->lock, flags);
rcar_gen3_phy_usb2_irqs_unmask(channel, masked_irq_bits);
rcar_gen3_init_otg_phase1(channel);
channel->otg_initializing = false;
wake_up_all(&channel->otg_init_done);
}
if (channel->phy_data->vblvl_ctrl) {
/* SIDDQ mode release */
@ -568,7 +705,10 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
rphy->initialized = true;
return 0;
unlock:
spin_unlock_irqrestore(&channel->lock, flags);
return ret;
}
static int rcar_gen3_phy_usb2_exit(struct phy *p)
@ -576,9 +716,32 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
int retries = NUM_OF_PHYS;
unsigned long flags;
u32 val;
int ret;
guard(spinlock_irqsave)(&channel->lock);
spin_lock_irqsave(&channel->lock, flags);
while (retries-- && channel->otg_initializing) {
spin_unlock_irqrestore(&channel->lock, flags);
ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
USB2_OTG_INIT_TIMEOUT);
ret = ret ? 0 : -ETIMEDOUT;
if (ret && !retries)
return ret;
spin_lock_irqsave(&channel->lock, flags);
}
/* If another thread started a new initialization just return -EBUSY. */
if (channel->otg_initializing) {
ret = -EBUSY;
goto unlock;
} else {
ret = 0;
}
rphy->initialized = false;
@ -588,7 +751,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
val &= ~USB2_INT_ENABLE_UCOM_INTEN;
writel(val, usb2_base + USB2_INT_ENABLE);
return 0;
unlock:
spin_unlock_irqrestore(&channel->lock, flags);
return ret;
}
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
@ -596,8 +761,10 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
int retries = NUM_OF_PHYS;
unsigned long flags;
u32 val;
int ret = 0;
int ret;
if (channel->vbus && !channel->otg_internal_reg) {
ret = regulator_enable(channel->vbus);
@ -605,7 +772,27 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
return ret;
}
guard(spinlock_irqsave)(&channel->lock);
spin_lock_irqsave(&channel->lock, flags);
while (retries-- && channel->otg_initializing) {
spin_unlock_irqrestore(&channel->lock, flags);
ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
USB2_OTG_INIT_TIMEOUT);
ret = ret ? 0 : -ETIMEDOUT;
if (ret && !retries)
goto disable_regulator;
spin_lock_irqsave(&channel->lock, flags);
}
/* If another thread started a new initialization just return -EBUSY. */
if (channel->otg_initializing) {
ret = -EBUSY;
goto unlock;
} else {
ret = 0;
}
if (!rcar_gen3_are_all_rphys_power_off(channel))
goto out;
@ -620,27 +807,59 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
/* The powered flag should be set for any other phys anyway */
rphy->powered = true;
return 0;
unlock:
spin_unlock_irqrestore(&channel->lock, flags);
disable_regulator:
if (ret && channel->vbus && !channel->otg_internal_reg)
regulator_disable(channel->vbus);
return ret;
}
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
{
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
int ret = 0;
int retries = NUM_OF_PHYS;
unsigned long flags;
int ret;
scoped_guard(spinlock_irqsave, &channel->lock) {
rphy->powered = false;
spin_lock_irqsave(&channel->lock, flags);
if (rcar_gen3_are_all_rphys_power_off(channel)) {
u32 val = readl(channel->base + USB2_USBCTR);
while (retries-- && channel->otg_initializing) {
spin_unlock_irqrestore(&channel->lock, flags);
val |= USB2_USBCTR_PLL_RST;
writel(val, channel->base + USB2_USBCTR);
}
ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
USB2_OTG_INIT_TIMEOUT);
ret = ret ? 0 : -ETIMEDOUT;
if (ret && !retries)
return ret;
spin_lock_irqsave(&channel->lock, flags);
}
if (channel->vbus && !channel->otg_internal_reg)
/* If another thread started a new initialization just return -EBUSY. */
if (channel->otg_initializing) {
ret = -EBUSY;
goto unlock;
} else {
ret = 0;
}
rphy->powered = false;
if (rcar_gen3_are_all_rphys_power_off(channel)) {
u32 val = readl(channel->base + USB2_USBCTR);
val |= USB2_USBCTR_PLL_RST;
writel(val, channel->base + USB2_USBCTR);
}
unlock:
spin_unlock_irqrestore(&channel->lock, flags);
if (!ret && channel->vbus && !channel->otg_internal_reg)
ret = regulator_disable(channel->vbus);
return ret;
@ -1022,6 +1241,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
return ret;
spin_lock_init(&channel->lock);
init_waitqueue_head(&channel->otg_init_done);
for (i = 0; i < NUM_OF_PHYS; i++) {
channel->rphys[i].phy = devm_phy_create(dev, NULL,
channel->phy_data->phy_usb2_ops);