Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.9-rc4 229 lines 5.6 kB view raw
1/* 2 * PCIe host controller driver for HiSilicon SoCs 3 * 4 * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com 5 * 6 * Authors: Zhou Wang <wangzhou1@hisilicon.com> 7 * Dacai Zhu <zhudacai@hisilicon.com> 8 * Gabriele Paoloni <gabriele.paoloni@huawei.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14#include <linux/interrupt.h> 15#include <linux/init.h> 16#include <linux/mfd/syscon.h> 17#include <linux/of_address.h> 18#include <linux/of_pci.h> 19#include <linux/platform_device.h> 20#include <linux/of_device.h> 21#include <linux/regmap.h> 22 23#include "pcie-designware.h" 24 25#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 26#define PCIE_HIP06_CTRL_OFF 0x1000 27#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) 28#define PCIE_LTSSM_LINKUP_STATE 0x11 29#define PCIE_LTSSM_STATE_MASK 0x3F 30 31#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) 32 33struct hisi_pcie; 34 35struct pcie_soc_ops { 36 int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); 37}; 38 39struct hisi_pcie { 40 struct pcie_port pp; /* pp.dbi_base is DT rc_dbi */ 41 struct regmap *subctrl; 42 u32 port_id; 43 struct pcie_soc_ops *soc_ops; 44}; 45 46/* HipXX PCIe host only supports 32-bit config access */ 47static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, 48 u32 *val) 49{ 50 u32 reg; 51 u32 reg_val; 52 void *walker = &reg_val; 53 54 walker += (where & 0x3); 55 reg = where & ~0x3; 56 reg_val = dw_pcie_readl_rc(pp, reg); 57 58 if (size == 1) 59 *val = *(u8 __force *) walker; 60 else if (size == 2) 61 *val = *(u16 __force *) walker; 62 else if (size == 4) 63 *val = reg_val; 64 else 65 return PCIBIOS_BAD_REGISTER_NUMBER; 66 67 return PCIBIOS_SUCCESSFUL; 68} 69 70/* HipXX PCIe host only supports 32-bit config access */ 71static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, 72 u32 val) 73{ 74 u32 reg_val; 75 u32 reg; 76 void *walker = &reg_val; 77 78 walker += (where & 0x3); 79 reg = where & ~0x3; 80 if (size == 4) 81 dw_pcie_writel_rc(pp, reg, val); 82 else if (size == 2) { 83 reg_val = dw_pcie_readl_rc(pp, reg); 84 *(u16 __force *) walker = val; 85 dw_pcie_writel_rc(pp, reg, reg_val); 86 } else if (size == 1) { 87 reg_val = dw_pcie_readl_rc(pp, reg); 88 *(u8 __force *) walker = val; 89 dw_pcie_writel_rc(pp, reg, reg_val); 90 } else 91 return PCIBIOS_BAD_REGISTER_NUMBER; 92 93 return PCIBIOS_SUCCESSFUL; 94} 95 96static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) 97{ 98 u32 val; 99 100 regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + 101 0x100 * hisi_pcie->port_id, &val); 102 103 return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); 104} 105 106static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) 107{ 108 struct pcie_port *pp = &hisi_pcie->pp; 109 u32 val; 110 111 val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4); 112 113 return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); 114} 115 116static int hisi_pcie_link_up(struct pcie_port *pp) 117{ 118 struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); 119 120 return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); 121} 122 123static struct pcie_host_ops hisi_pcie_host_ops = { 124 .rd_own_conf = hisi_pcie_cfg_read, 125 .wr_own_conf = hisi_pcie_cfg_write, 126 .link_up = hisi_pcie_link_up, 127}; 128 129static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, 130 struct platform_device *pdev) 131{ 132 struct pcie_port *pp = &hisi_pcie->pp; 133 struct device *dev = pp->dev; 134 int ret; 135 u32 port_id; 136 137 if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { 138 dev_err(dev, "failed to read port-id\n"); 139 return -EINVAL; 140 } 141 if (port_id > 3) { 142 dev_err(dev, "Invalid port-id: %d\n", port_id); 143 return -EINVAL; 144 } 145 hisi_pcie->port_id = port_id; 146 147 pp->ops = &hisi_pcie_host_ops; 148 149 ret = dw_pcie_host_init(pp); 150 if (ret) { 151 dev_err(dev, "failed to initialize host\n"); 152 return ret; 153 } 154 155 return 0; 156} 157 158static int hisi_pcie_probe(struct platform_device *pdev) 159{ 160 struct device *dev = &pdev->dev; 161 struct hisi_pcie *hisi_pcie; 162 struct pcie_port *pp; 163 const struct of_device_id *match; 164 struct resource *reg; 165 struct device_driver *driver; 166 int ret; 167 168 hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); 169 if (!hisi_pcie) 170 return -ENOMEM; 171 172 pp = &hisi_pcie->pp; 173 pp->dev = dev; 174 driver = dev->driver; 175 176 match = of_match_device(driver->of_match_table, dev); 177 hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data; 178 179 hisi_pcie->subctrl = 180 syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); 181 if (IS_ERR(hisi_pcie->subctrl)) { 182 dev_err(dev, "cannot get subctrl base\n"); 183 return PTR_ERR(hisi_pcie->subctrl); 184 } 185 186 reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); 187 pp->dbi_base = devm_ioremap_resource(dev, reg); 188 if (IS_ERR(pp->dbi_base)) { 189 dev_err(dev, "cannot get rc_dbi base\n"); 190 return PTR_ERR(pp->dbi_base); 191 } 192 193 ret = hisi_add_pcie_port(hisi_pcie, pdev); 194 if (ret) 195 return ret; 196 197 dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n"); 198 199 return 0; 200} 201 202static struct pcie_soc_ops hip05_ops = { 203 &hisi_pcie_link_up_hip05 204}; 205 206static struct pcie_soc_ops hip06_ops = { 207 &hisi_pcie_link_up_hip06 208}; 209 210static const struct of_device_id hisi_pcie_of_match[] = { 211 { 212 .compatible = "hisilicon,hip05-pcie", 213 .data = (void *) &hip05_ops, 214 }, 215 { 216 .compatible = "hisilicon,hip06-pcie", 217 .data = (void *) &hip06_ops, 218 }, 219 {}, 220}; 221 222static struct platform_driver hisi_pcie_driver = { 223 .probe = hisi_pcie_probe, 224 .driver = { 225 .name = "hisi-pcie", 226 .of_match_table = hisi_pcie_of_match, 227 }, 228}; 229builtin_platform_driver(hisi_pcie_driver);