cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ehci-brcm.c (6987B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (c) 2020, Broadcom */
      3
      4#include <linux/clk.h>
      5#include <linux/dma-mapping.h>
      6#include <linux/err.h>
      7#include <linux/kernel.h>
      8#include <linux/io.h>
      9#include <linux/module.h>
     10#include <linux/platform_device.h>
     11#include <linux/usb.h>
     12#include <linux/usb/hcd.h>
     13#include <linux/iopoll.h>
     14
     15#include "ehci.h"
     16
     17#define hcd_to_ehci_priv(h) ((struct brcm_priv *)hcd_to_ehci(h)->priv)
     18
     19struct brcm_priv {
     20	struct clk *clk;
     21};
     22
     23/*
     24 * ehci_brcm_wait_for_sof
     25 * Wait for start of next microframe, then wait extra delay microseconds
     26 */
     27static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay)
     28{
     29	u32 frame_idx = ehci_readl(ehci, &ehci->regs->frame_index);
     30	u32 val;
     31	int res;
     32
     33	/* Wait for next microframe (every 125 usecs) */
     34	res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val,
     35					 val != frame_idx, 1, 130);
     36	if (res)
     37		ehci_err(ehci, "Error waiting for SOF\n");
     38	udelay(delay);
     39}
     40
     41/*
     42 * ehci_brcm_hub_control
     43 * The EHCI controller has a bug where it can violate the SOF
     44 * interval between the first two SOF's transmitted after resume
     45 * if the resume occurs near the end of the microframe. This causees
     46 * the controller to detect babble on the suspended port and
     47 * will eventually cause the controller to reset the port.
     48 * The fix is to Intercept the echi-hcd request to complete RESUME and
     49 * align it to the start of the next microframe.
     50 * See SWLINUX-1909 for more details
     51 */
     52static int ehci_brcm_hub_control(
     53	struct usb_hcd	*hcd,
     54	u16		typeReq,
     55	u16		wValue,
     56	u16		wIndex,
     57	char		*buf,
     58	u16		wLength)
     59{
     60	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
     61	int		ports = HCS_N_PORTS(ehci->hcs_params);
     62	u32 __iomem	*status_reg;
     63	unsigned long flags;
     64	int retval, irq_disabled = 0;
     65	u32 temp;
     66
     67	temp = (wIndex & 0xff) - 1;
     68	if (temp >= HCS_N_PORTS_MAX)	/* Avoid index-out-of-bounds warning */
     69		temp = 0;
     70	status_reg = &ehci->regs->port_status[temp];
     71
     72	/*
     73	 * RESUME is cleared when GetPortStatus() is called 20ms after start
     74	 * of RESUME
     75	 */
     76	if ((typeReq == GetPortStatus) &&
     77	    (wIndex && wIndex <= ports) &&
     78	    ehci->reset_done[wIndex-1] &&
     79	    time_after_eq(jiffies, ehci->reset_done[wIndex-1]) &&
     80	    (ehci_readl(ehci, status_reg) & PORT_RESUME)) {
     81
     82		/*
     83		 * to make sure we are not interrupted until RESUME bit
     84		 * is cleared, disable interrupts on current CPU
     85		 */
     86		ehci_dbg(ehci, "SOF alignment workaround\n");
     87		irq_disabled = 1;
     88		local_irq_save(flags);
     89		ehci_brcm_wait_for_sof(ehci, 5);
     90	}
     91	retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
     92	if (irq_disabled)
     93		local_irq_restore(flags);
     94	return retval;
     95}
     96
     97static int ehci_brcm_reset(struct usb_hcd *hcd)
     98{
     99	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
    100	int len;
    101
    102	ehci->big_endian_mmio = 1;
    103
    104	ehci->caps = (void __iomem *)hcd->regs;
    105	len = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
    106	ehci->regs = (void __iomem *)(hcd->regs + len);
    107
    108	/* This fixes the lockup during reboot due to prior interrupts */
    109	ehci_writel(ehci, CMD_RESET, &ehci->regs->command);
    110	mdelay(10);
    111
    112	/*
    113	 * SWLINUX-1705: Avoid OUT packet underflows during high memory
    114	 *   bus usage
    115	 */
    116	ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]);
    117	ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]);
    118
    119	return ehci_setup(hcd);
    120}
    121
    122static struct hc_driver __read_mostly ehci_brcm_hc_driver;
    123
    124static const struct ehci_driver_overrides brcm_overrides __initconst = {
    125	.reset = ehci_brcm_reset,
    126	.extra_priv_size = sizeof(struct brcm_priv),
    127};
    128
    129static int ehci_brcm_probe(struct platform_device *pdev)
    130{
    131	struct device *dev = &pdev->dev;
    132	struct resource *res_mem;
    133	struct brcm_priv *priv;
    134	struct usb_hcd *hcd;
    135	int irq;
    136	int err;
    137
    138	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
    139	if (err)
    140		return err;
    141
    142	irq = platform_get_irq(pdev, 0);
    143	if (irq <= 0)
    144		return irq ? irq : -EINVAL;
    145
    146	/* Hook the hub control routine to work around a bug */
    147	ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control;
    148
    149	/* initialize hcd */
    150	hcd = usb_create_hcd(&ehci_brcm_hc_driver, dev, dev_name(dev));
    151	if (!hcd)
    152		return -ENOMEM;
    153
    154	platform_set_drvdata(pdev, hcd);
    155	priv = hcd_to_ehci_priv(hcd);
    156
    157	priv->clk = devm_clk_get_optional(dev, NULL);
    158	if (IS_ERR(priv->clk)) {
    159		err = PTR_ERR(priv->clk);
    160		goto err_hcd;
    161	}
    162
    163	err = clk_prepare_enable(priv->clk);
    164	if (err)
    165		goto err_hcd;
    166
    167	hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res_mem);
    168	if (IS_ERR(hcd->regs)) {
    169		err = PTR_ERR(hcd->regs);
    170		goto err_clk;
    171	}
    172	hcd->rsrc_start = res_mem->start;
    173	hcd->rsrc_len = resource_size(res_mem);
    174	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
    175	if (err)
    176		goto err_clk;
    177
    178	device_wakeup_enable(hcd->self.controller);
    179	device_enable_async_suspend(hcd->self.controller);
    180
    181	return 0;
    182
    183err_clk:
    184	clk_disable_unprepare(priv->clk);
    185err_hcd:
    186	usb_put_hcd(hcd);
    187
    188	return err;
    189}
    190
    191static int ehci_brcm_remove(struct platform_device *dev)
    192{
    193	struct usb_hcd *hcd = platform_get_drvdata(dev);
    194	struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
    195
    196	usb_remove_hcd(hcd);
    197	clk_disable_unprepare(priv->clk);
    198	usb_put_hcd(hcd);
    199	return 0;
    200}
    201
    202static int __maybe_unused ehci_brcm_suspend(struct device *dev)
    203{
    204	int ret;
    205	struct usb_hcd *hcd = dev_get_drvdata(dev);
    206	struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
    207	bool do_wakeup = device_may_wakeup(dev);
    208
    209	ret = ehci_suspend(hcd, do_wakeup);
    210	if (ret)
    211		return ret;
    212	clk_disable_unprepare(priv->clk);
    213	return 0;
    214}
    215
    216static int __maybe_unused ehci_brcm_resume(struct device *dev)
    217{
    218	struct usb_hcd *hcd = dev_get_drvdata(dev);
    219	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
    220	struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
    221	int err;
    222
    223	err = clk_prepare_enable(priv->clk);
    224	if (err)
    225		return err;
    226	/*
    227	 * SWLINUX-1705: Avoid OUT packet underflows during high memory
    228	 *   bus usage
    229	 */
    230	ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]);
    231	ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]);
    232
    233	ehci_resume(hcd, false);
    234
    235	pm_runtime_disable(dev);
    236	pm_runtime_set_active(dev);
    237	pm_runtime_enable(dev);
    238
    239	return 0;
    240}
    241
    242static SIMPLE_DEV_PM_OPS(ehci_brcm_pm_ops, ehci_brcm_suspend,
    243		ehci_brcm_resume);
    244
    245static const struct of_device_id brcm_ehci_of_match[] = {
    246	{ .compatible = "brcm,ehci-brcm-v2", },
    247	{ .compatible = "brcm,bcm7445-ehci", },
    248	{}
    249};
    250
    251static struct platform_driver ehci_brcm_driver = {
    252	.probe		= ehci_brcm_probe,
    253	.remove		= ehci_brcm_remove,
    254	.shutdown	= usb_hcd_platform_shutdown,
    255	.driver		= {
    256		.name	= "ehci-brcm",
    257		.pm	= &ehci_brcm_pm_ops,
    258		.of_match_table = brcm_ehci_of_match,
    259	}
    260};
    261
    262static int __init ehci_brcm_init(void)
    263{
    264	if (usb_disabled())
    265		return -ENODEV;
    266
    267	ehci_init_driver(&ehci_brcm_hc_driver, &brcm_overrides);
    268	return platform_driver_register(&ehci_brcm_driver);
    269}
    270module_init(ehci_brcm_init);
    271
    272static void __exit ehci_brcm_exit(void)
    273{
    274	platform_driver_unregister(&ehci_brcm_driver);
    275}
    276module_exit(ehci_brcm_exit);
    277
    278MODULE_ALIAS("platform:ehci-brcm");
    279MODULE_DESCRIPTION("EHCI Broadcom STB driver");
    280MODULE_AUTHOR("Al Cooper");
    281MODULE_LICENSE("GPL");