From 4617721c502b2ddaa4e324e86da4997edf738fa5 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 2 Sep 2026 09:55:01 -0400 Subject: ftrace: Synchronize the initialization of ftrace_ops There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt --- drivers/bcma/driver_pci.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 drivers/bcma/driver_pci.c (limited to 'drivers/bcma/driver_pci.c') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c new file mode 100644 index 000000000..12b2cc9a3 --- /dev/null +++ b/drivers/bcma/driver_pci.c @@ -0,0 +1,306 @@ +/* + * Broadcom specific AMBA + * PCI Core + * + * Copyright 2005, 2011, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * Copyright 2011, 2012, Hauke Mehrtens + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include +#include + +/************************************************** + * R/W ops. + **************************************************/ + +u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address) +{ + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA); +} + +static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) +{ + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); +} + +static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy) +{ + u32 v; + int i; + + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR << + BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + v |= BCMA_CORE_PCI_MDIODATA_TA; + v |= (phy << 4); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); + + udelay(10); + for (i = 0; i < 200; i++) { + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) + break; + usleep_range(1000, 2000); + } +} + +static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address) +{ + int max_retries = 10; + u16 ret = 0; + u32 v; + int i; + + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); + + if (pc->core->id.rev >= 10) { + max_retries = 200; + bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); + } + + v |= BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_READ; + v |= BCMA_CORE_PCI_MDIODATA_TA; + + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); + /* Wait for the device to complete the transaction */ + udelay(10); + for (i = 0; i < max_retries; i++) { + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) { + udelay(10); + ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA); + break; + } + usleep_range(1000, 2000); + } + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); + return ret; +} + +static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device, + u8 address, u16 data) +{ + int max_retries = 10; + u32 v; + int i; + + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); + + if (pc->core->id.rev >= 10) { + max_retries = 200; + bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); + } + + v |= BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= BCMA_CORE_PCI_MDIODATA_TA; + v |= data; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); + /* Wait for the device to complete the transaction */ + udelay(10); + for (i = 0; i < max_retries; i++) { + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) + break; + usleep_range(1000, 2000); + } + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); +} + +static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device, + u8 address, u16 data) +{ + bcma_pcie_mdio_write(pc, device, address, data); + return bcma_pcie_mdio_read(pc, device, address); +} + +/************************************************** + * Early init. + **************************************************/ + +static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc) +{ + struct bcma_device *core = pc->core; + u16 val16, core_index; + uint regoff; + + regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET); + core_index = (u16)core->core_index; + + val16 = pcicore_read16(pc, regoff); + if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT) + != core_index) { + val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) | + (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK); + pcicore_write16(pc, regoff, val16); + } +} + +/* + * Apply some early fixes required before accessing SPROM. + * See also si_pci_fixcfg. + */ +void bcma_core_pci_early_init(struct bcma_drv_pci *pc) +{ + if (pc->early_setup_done) + return; + + pc->hostmode = bcma_core_pci_is_in_hostmode(pc); + if (pc->hostmode) + goto out; + + bcma_core_pci_fixcfg(pc); + +out: + pc->early_setup_done = true; +} + +/************************************************** + * Workarounds. + **************************************************/ + +static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc) +{ + u32 tmp; + + tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG); + if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT) + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE | + BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY; + else + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE; +} + +static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) +{ + u16 tmp; + + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX, + BCMA_CORE_PCI_SERDES_RX_CTRL, + bcma_pcicore_polarity_workaround(pc)); + tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL); + if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN) + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL, + tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN); +} + +/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */ +/* Needs to happen when coming out of 'standby'/'hibernate' */ +static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) +{ + u16 val16; + uint regoff; + + regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG); + + val16 = pcicore_read16(pc, regoff); + + if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) { + val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST; + pcicore_write16(pc, regoff, val16); + } +} + +/************************************************** + * Init. + **************************************************/ + +static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) +{ + bcma_pcicore_serdes_workaround(pc); + bcma_core_pci_config_fixup(pc); +} + +void bcma_core_pci_init(struct bcma_drv_pci *pc) +{ + if (pc->setup_done) + return; + + bcma_core_pci_early_init(pc); + + if (pc->hostmode) + bcma_core_pci_hostmode_init(pc); + else + bcma_core_pci_clientmode_init(pc); +} + +void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) +{ + struct bcma_drv_pci *pc; + u16 data; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) { + data = up ? 0x74 : 0x7C; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) { + data = up ? 0x75 : 0x7D; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } +} +EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); + +static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) +{ + u32 w; + + w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); + if (extend) + w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND; + else + w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND; + bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w); + bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); +} + +void bcma_core_pci_up(struct bcma_drv_pci *pc) +{ + bcma_core_pci_extend_L1timer(pc, true); +} + +void bcma_core_pci_down(struct bcma_drv_pci *pc) +{ + bcma_core_pci_extend_L1timer(pc, false); +} -- cgit v1.3.1