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

pcie-dw-rockchip.c (9759B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * PCIe host controller driver for Rockchip SoCs.
      4 *
      5 * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
      6 *		http://www.rock-chips.com
      7 *
      8 * Author: Simon Xue <xxm@rock-chips.com>
      9 */
     10
     11#include <linux/clk.h>
     12#include <linux/gpio/consumer.h>
     13#include <linux/irqchip/chained_irq.h>
     14#include <linux/irqdomain.h>
     15#include <linux/mfd/syscon.h>
     16#include <linux/module.h>
     17#include <linux/of_device.h>
     18#include <linux/of_irq.h>
     19#include <linux/phy/phy.h>
     20#include <linux/platform_device.h>
     21#include <linux/regmap.h>
     22#include <linux/reset.h>
     23
     24#include "pcie-designware.h"
     25
     26/*
     27 * The upper 16 bits of PCIE_CLIENT_CONFIG are a write
     28 * mask for the lower 16 bits.
     29 */
     30#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
     31#define HIWORD_UPDATE_BIT(val)	HIWORD_UPDATE(val, val)
     32#define HIWORD_DISABLE_BIT(val)	HIWORD_UPDATE(val, ~val)
     33
     34#define to_rockchip_pcie(x) dev_get_drvdata((x)->dev)
     35
     36#define PCIE_CLIENT_RC_MODE		HIWORD_UPDATE_BIT(0x40)
     37#define PCIE_CLIENT_ENABLE_LTSSM	HIWORD_UPDATE_BIT(0xc)
     38#define PCIE_SMLH_LINKUP		BIT(16)
     39#define PCIE_RDLH_LINKUP		BIT(17)
     40#define PCIE_LINKUP			(PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP)
     41#define PCIE_L0S_ENTRY			0x11
     42#define PCIE_CLIENT_GENERAL_CONTROL	0x0
     43#define PCIE_CLIENT_INTR_STATUS_LEGACY	0x8
     44#define PCIE_CLIENT_INTR_MASK_LEGACY	0x1c
     45#define PCIE_CLIENT_GENERAL_DEBUG	0x104
     46#define PCIE_CLIENT_HOT_RESET_CTRL	0x180
     47#define PCIE_CLIENT_LTSSM_STATUS	0x300
     48#define PCIE_LTSSM_ENABLE_ENHANCE	BIT(4)
     49#define PCIE_LTSSM_STATUS_MASK		GENMASK(5, 0)
     50
     51struct rockchip_pcie {
     52	struct dw_pcie			pci;
     53	void __iomem			*apb_base;
     54	struct phy			*phy;
     55	struct clk_bulk_data		*clks;
     56	unsigned int			clk_cnt;
     57	struct reset_control		*rst;
     58	struct gpio_desc		*rst_gpio;
     59	struct regulator                *vpcie3v3;
     60	struct irq_domain		*irq_domain;
     61};
     62
     63static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip,
     64					     u32 reg)
     65{
     66	return readl_relaxed(rockchip->apb_base + reg);
     67}
     68
     69static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip,
     70						u32 val, u32 reg)
     71{
     72	writel_relaxed(val, rockchip->apb_base + reg);
     73}
     74
     75static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
     76{
     77	struct irq_chip *chip = irq_desc_get_chip(desc);
     78	struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc);
     79	unsigned long reg, hwirq;
     80
     81	chained_irq_enter(chip, desc);
     82
     83	reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_LEGACY);
     84
     85	for_each_set_bit(hwirq, &reg, 4)
     86		generic_handle_domain_irq(rockchip->irq_domain, hwirq);
     87
     88	chained_irq_exit(chip, desc);
     89}
     90
     91static void rockchip_intx_mask(struct irq_data *data)
     92{
     93	rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
     94				 HIWORD_UPDATE_BIT(BIT(data->hwirq)),
     95				 PCIE_CLIENT_INTR_MASK_LEGACY);
     96};
     97
     98static void rockchip_intx_unmask(struct irq_data *data)
     99{
    100	rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
    101				 HIWORD_DISABLE_BIT(BIT(data->hwirq)),
    102				 PCIE_CLIENT_INTR_MASK_LEGACY);
    103};
    104
    105static struct irq_chip rockchip_intx_irq_chip = {
    106	.name			= "INTx",
    107	.irq_mask		= rockchip_intx_mask,
    108	.irq_unmask		= rockchip_intx_unmask,
    109	.flags			= IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
    110};
    111
    112static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
    113				  irq_hw_number_t hwirq)
    114{
    115	irq_set_chip_and_handler(irq, &rockchip_intx_irq_chip, handle_level_irq);
    116	irq_set_chip_data(irq, domain->host_data);
    117
    118	return 0;
    119}
    120
    121static const struct irq_domain_ops intx_domain_ops = {
    122	.map = rockchip_pcie_intx_map,
    123};
    124
    125static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip)
    126{
    127	struct device *dev = rockchip->pci.dev;
    128	struct device_node *intc;
    129
    130	intc = of_get_child_by_name(dev->of_node, "legacy-interrupt-controller");
    131	if (!intc) {
    132		dev_err(dev, "missing child interrupt-controller node\n");
    133		return -EINVAL;
    134	}
    135
    136	rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX,
    137						    &intx_domain_ops, rockchip);
    138	of_node_put(intc);
    139	if (!rockchip->irq_domain) {
    140		dev_err(dev, "failed to get a INTx IRQ domain\n");
    141		return -EINVAL;
    142	}
    143
    144	return 0;
    145}
    146
    147static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
    148{
    149	rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
    150				 PCIE_CLIENT_GENERAL_CONTROL);
    151}
    152
    153static int rockchip_pcie_link_up(struct dw_pcie *pci)
    154{
    155	struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
    156	u32 val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS);
    157
    158	if ((val & PCIE_LINKUP) == PCIE_LINKUP &&
    159	    (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY)
    160		return 1;
    161
    162	return 0;
    163}
    164
    165static int rockchip_pcie_start_link(struct dw_pcie *pci)
    166{
    167	struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
    168
    169	/* Reset device */
    170	gpiod_set_value_cansleep(rockchip->rst_gpio, 0);
    171
    172	rockchip_pcie_enable_ltssm(rockchip);
    173
    174	/*
    175	 * PCIe requires the refclk to be stable for 100µs prior to releasing
    176	 * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI
    177	 * Express Card Electromechanical Specification, 1.1. However, we don't
    178	 * know if the refclk is coming from RC's PHY or external OSC. If it's
    179	 * from RC, so enabling LTSSM is the just right place to release #PERST.
    180	 * We need more extra time as before, rather than setting just
    181	 * 100us as we don't know how long should the device need to reset.
    182	 */
    183	msleep(100);
    184	gpiod_set_value_cansleep(rockchip->rst_gpio, 1);
    185
    186	return 0;
    187}
    188
    189static int rockchip_pcie_host_init(struct pcie_port *pp)
    190{
    191	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
    192	struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
    193	struct device *dev = rockchip->pci.dev;
    194	u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE);
    195	int irq, ret;
    196
    197	irq = of_irq_get_byname(dev->of_node, "legacy");
    198	if (irq < 0)
    199		return irq;
    200
    201	ret = rockchip_pcie_init_irq_domain(rockchip);
    202	if (ret < 0)
    203		dev_err(dev, "failed to init irq domain\n");
    204
    205	irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler,
    206					 rockchip);
    207
    208	/* LTSSM enable control mode */
    209	rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
    210
    211	rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE,
    212				 PCIE_CLIENT_GENERAL_CONTROL);
    213
    214	return 0;
    215}
    216
    217static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
    218	.host_init = rockchip_pcie_host_init,
    219};
    220
    221static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip)
    222{
    223	struct device *dev = rockchip->pci.dev;
    224	int ret;
    225
    226	ret = devm_clk_bulk_get_all(dev, &rockchip->clks);
    227	if (ret < 0)
    228		return ret;
    229
    230	rockchip->clk_cnt = ret;
    231
    232	return clk_bulk_prepare_enable(rockchip->clk_cnt, rockchip->clks);
    233}
    234
    235static int rockchip_pcie_resource_get(struct platform_device *pdev,
    236				      struct rockchip_pcie *rockchip)
    237{
    238	rockchip->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
    239	if (IS_ERR(rockchip->apb_base))
    240		return PTR_ERR(rockchip->apb_base);
    241
    242	rockchip->rst_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
    243						     GPIOD_OUT_HIGH);
    244	if (IS_ERR(rockchip->rst_gpio))
    245		return PTR_ERR(rockchip->rst_gpio);
    246
    247	rockchip->rst = devm_reset_control_array_get_exclusive(&pdev->dev);
    248	if (IS_ERR(rockchip->rst))
    249		return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst),
    250				     "failed to get reset lines\n");
    251
    252	return 0;
    253}
    254
    255static int rockchip_pcie_phy_init(struct rockchip_pcie *rockchip)
    256{
    257	struct device *dev = rockchip->pci.dev;
    258	int ret;
    259
    260	rockchip->phy = devm_phy_get(dev, "pcie-phy");
    261	if (IS_ERR(rockchip->phy))
    262		return dev_err_probe(dev, PTR_ERR(rockchip->phy),
    263				     "missing PHY\n");
    264
    265	ret = phy_init(rockchip->phy);
    266	if (ret < 0)
    267		return ret;
    268
    269	ret = phy_power_on(rockchip->phy);
    270	if (ret)
    271		phy_exit(rockchip->phy);
    272
    273	return ret;
    274}
    275
    276static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip)
    277{
    278	phy_exit(rockchip->phy);
    279	phy_power_off(rockchip->phy);
    280}
    281
    282static const struct dw_pcie_ops dw_pcie_ops = {
    283	.link_up = rockchip_pcie_link_up,
    284	.start_link = rockchip_pcie_start_link,
    285};
    286
    287static int rockchip_pcie_probe(struct platform_device *pdev)
    288{
    289	struct device *dev = &pdev->dev;
    290	struct rockchip_pcie *rockchip;
    291	struct pcie_port *pp;
    292	int ret;
    293
    294	rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
    295	if (!rockchip)
    296		return -ENOMEM;
    297
    298	platform_set_drvdata(pdev, rockchip);
    299
    300	rockchip->pci.dev = dev;
    301	rockchip->pci.ops = &dw_pcie_ops;
    302
    303	pp = &rockchip->pci.pp;
    304	pp->ops = &rockchip_pcie_host_ops;
    305
    306	ret = rockchip_pcie_resource_get(pdev, rockchip);
    307	if (ret)
    308		return ret;
    309
    310	ret = reset_control_assert(rockchip->rst);
    311	if (ret)
    312		return ret;
    313
    314	/* DON'T MOVE ME: must be enable before PHY init */
    315	rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
    316	if (IS_ERR(rockchip->vpcie3v3)) {
    317		if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV)
    318			return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3),
    319					"failed to get vpcie3v3 regulator\n");
    320		rockchip->vpcie3v3 = NULL;
    321	} else {
    322		ret = regulator_enable(rockchip->vpcie3v3);
    323		if (ret) {
    324			dev_err(dev, "failed to enable vpcie3v3 regulator\n");
    325			return ret;
    326		}
    327	}
    328
    329	ret = rockchip_pcie_phy_init(rockchip);
    330	if (ret)
    331		goto disable_regulator;
    332
    333	ret = reset_control_deassert(rockchip->rst);
    334	if (ret)
    335		goto deinit_phy;
    336
    337	ret = rockchip_pcie_clk_init(rockchip);
    338	if (ret)
    339		goto deinit_phy;
    340
    341	ret = dw_pcie_host_init(pp);
    342	if (!ret)
    343		return 0;
    344
    345	clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
    346deinit_phy:
    347	rockchip_pcie_phy_deinit(rockchip);
    348disable_regulator:
    349	if (rockchip->vpcie3v3)
    350		regulator_disable(rockchip->vpcie3v3);
    351
    352	return ret;
    353}
    354
    355static const struct of_device_id rockchip_pcie_of_match[] = {
    356	{ .compatible = "rockchip,rk3568-pcie", },
    357	{},
    358};
    359
    360static struct platform_driver rockchip_pcie_driver = {
    361	.driver = {
    362		.name	= "rockchip-dw-pcie",
    363		.of_match_table = rockchip_pcie_of_match,
    364		.suppress_bind_attrs = true,
    365	},
    366	.probe = rockchip_pcie_probe,
    367};
    368builtin_platform_driver(rockchip_pcie_driver);