Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2635da9f authored by Yan He's avatar Yan He
Browse files

msm: ep_pcie: add PCIe endpoint driver



The MSM PCIe endpoint driver enables the PCIe core in endpoint mode
and handles the control signaling with PCIe root complex on host
side.

Change-Id: Ifc2735e061820762c6040eda44089a2dc26fc065
Signed-off-by: default avatarYan He <yanhe@codeaurora.org>
parent 20a5b840
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
MSM PCI express endpoint

Required properties:
  - compatible: should be "qcom,pcie-ep".
  - reg: should contain PCIe register maps.
  - reg-names: indicates various resources passed to driver by name.
		Should be "msi", "dm_core", "elbi", "parf", "phy", "mmio".
		These correspond to different modules within the PCIe domain.
  - #address-cells: Should provide a value of 0.
  - interrupt-parent: Should be the PCIe device node itself here.
  - interrupts: Should be in the format <0 1 2> and it is an index to the
		interrupt-map that contains PCIe related interrupts.
  - #interrupt-cells: Should provide a value of 1.
  - #interrupt-map-mask: should provide a value of 0xffffffff.
  - interrupt-map:  Must create mapping for the number of interrupts
		    that are defined in above interrupts property.
		    For PCIe device node, it should define 6 mappings for
		    the corresponding PCIe interrupts supporting the
		    specification.
  - interrupt-names: indicates interrupts passed to driver by name.
		     Should be "int_pm_turnoff", "int_dstate_change",
				"int_l1sub_timeout", "int_link_up",
				"int_link_down", "int_bridge_flush_n".
  - perst-gpio: PERST GPIO specified by PCIe spec.
  - wake-gpio: WAKE GPIO specified by PCIe spec.
  - clkreq-gpio: CLKREQ GPIO specified by PCIe spec.
  - <supply-name>-supply: phandle to the regulator device tree node.
    Refer to the schematics for the corresponding voltage regulators.
    vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
    vreg-0.9-supply: phandle to the analog supply for the PCIe controller.

Optional Properties:
  - qcom,<supply-name>-voltage-level: specifies voltage levels for supply.
    Should be specified in pairs (max, min, optimal), units uV.
  - clock-names: list of names of clock inputs.
		     Should be "pcie_0_pipe_clk",
				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
				"pcie_0_ldo";
  - max-clock-frequency-hz: list of the maximum operating frequencies stored
				in the same order of clock names;
  - qcom,pcie-phy-ver: version of PCIe PHY.
  - qcom,pcie-link-speed: generation of PCIe link speed. The value could be
    1, 2 or 3.
  - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
    below optional properties:
	- qcom,msm-bus,name
	- qcom,msm-bus,num-cases
	- qcom,msm-bus,num-paths
	- qcom,msm-bus,vectors-KBps

Example:

	pcie_ep: qcom,pcie@bfffd000 {
		compatible = "qcom,pcie-ep";

		reg = <0xbfffd000 0x1000>,
			<0xbfffe000 0x1000>,
			<0xbffff000 0x1000>,
			<0xfc520000 0x2000>,
			<0xfc526000 0x1000>,
			<0xfc527000 0x1000>;
		reg-names = "msi", "dm_core", "elbi", "parf", "phy", "mmio";

		#address-cells = <0>;
		interrupt-parent = <&pcie_ep>;
		interrupts = <0 1 2 3 4 5>;
		#interrupt-cells = <1>;
		interrupt-map-mask = <0xffffffff>;
		interrupt-map = <0 &intc 0 44 0
				1 &intc 0 46 0
				2 &intc 0 47 0
				3 &intc 0 50 0
				4 &intc 0 51 0
				5 &intc 0 52 0>;
		interrupt-names = "int_pm_turnoff", "int_dstate_change",
				"int_l1sub_timeout", "int_link_up",
				"int_link_down", "int_bridge_flush_n";

		perst-gpio = <&msmgpio 65 0>;
		wake-gpio = <&msmgpio 61 0>;
		clkreq-gpio = <&msmgpio 64 0>;

		gdsc-vdd-supply = <&gdsc_pcie_0>;
		vreg-1.8-supply = <&pmd9635_l8>;
		vreg-0.9-supply = <&pmd9635_l4>;

		qcom,vreg-1.8-voltage-level = <1800000 1800000 1000>;
		qcom,vreg-0.9-voltage-level = <950000 950000 24000>;

		clock-names = "pcie_0_pipe_clk",
				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
				"pcie_0_ldo";
		max-clock-frequency-hz = <62500000>, <1000000>,
						<0>, <0>, <0>, <0>;

		qcom,msm-bus,name = "pcie-ep";
		qcom,msm-bus,num-cases = <2>;
		qcom,msm-bus,num-paths = <1>;
		qcom,msm-bus,vectors-KBps =
				<45 512 0 0>,
				<45 512 500 800>;

		qcom,pcie-link-speed = <1>;
	};
 No newline at end of file
+21 −0
Original line number Diff line number Diff line
@@ -70,6 +70,27 @@ config SPS
		2. Peripheral-to-Memory.
		3. Memory-to-Memory.

config EP_PCIE
	bool "PCIe Endpoint mode support"
	select GENERIC_ALLOCATOR
	help
	  PCIe controller is in endpoint mode.
	  It supports the APIs to clients as a service layer, and allows
	  clients to enable/disable PCIe link, configure the address
	  mapping for the access to host memory, trigger wake interrupt
	  on host side to wake up host, and trigger MSI to host side.

config EP_PCIE_HW
	bool "PCIe Endpoint HW driver"
	depends on EP_PCIE
	help
	  PCIe endpoint HW specific implementation.
	  It supports:
		1. link training with Root Complex.
		2. Address mapping.
		3. Sideband signaling.
		4. Power management.

config USB_BAM
	bool "USB BAM Driver"
	depends on SPS && USB_GADGET
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ obj-$(CONFIG_QPNP_POWER_ON) += qpnp-power-on.o
obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
obj-$(CONFIG_SPS) += sps/
obj-$(CONFIG_EP_PCIE) += ep_pcie/
obj-$(CONFIG_I2C_MSM_PROF_DBG) += i2c-msm-prof-dbg.o
obj-$(CONFIG_IPA) += ipa/
obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o
+3 −0
Original line number Diff line number Diff line
obj-$(CONFIG_EP_PCIE) += ep_pcie.o
obj-$(CONFIG_EP_PCIE_HW) += ep_pcie_core.o ep_pcie_phy.o ep_pcie_dbg.o
+232 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/*
 * MSM PCIe endpoint service layer.
 */
#include <linux/types.h>
#include <linux/list.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/msm_ep_pcie.h>

LIST_HEAD(head);

int ep_pcie_register_drv(struct ep_pcie_hw *handle)
{
	struct ep_pcie_hw *present;
	bool new = true;

	if (!handle) {
		pr_err("ep_pcie:%s: the input handle is NULL.",
			__func__);
		return -EINVAL;
	}

	list_for_each_entry(present, &head, node) {
		if (present->device_id == handle->device_id) {
			new = false;
			break;
		}
	}

	if (new) {
		list_add(&handle->node, &head);
		pr_debug("ep_pcie:%s: register a new driver for device 0x%x.",
			__func__, handle->device_id);
		return 0;
	} else {
		pr_debug(
			"ep_pcie:%s: driver to register for device 0x%x has already existed.",
			__func__, handle->device_id);
		return -EEXIST;
	}
}
EXPORT_SYMBOL(ep_pcie_register_drv);

int ep_pcie_deregister_drv(struct ep_pcie_hw *handle)
{
	struct ep_pcie_hw *present;
	bool found = false;

	if (!handle) {
		pr_err("ep_pcie:%s: the input handle is NULL.",
			__func__);
		return -EINVAL;
	}

	list_for_each_entry(present, &head, node) {
		if (present->device_id == handle->device_id) {
			found = true;
			list_del(&handle->node);
			break;
		}
	}

	if (found) {
		pr_debug("ep_pcie:%s: deregistered driver for device 0x%x.",
			__func__, handle->device_id);
		return 0;
	} else {
		pr_err("ep_pcie:%s: driver for device 0x%x does not exist.",
			__func__, handle->device_id);
		return -EEXIST;
	}
}
EXPORT_SYMBOL(ep_pcie_deregister_drv);

struct ep_pcie_hw *ep_pcie_get_phandle(u32 id)
{
	struct ep_pcie_hw *present;

	list_for_each_entry(present, &head, node) {
		if (present->device_id == id) {
			pr_debug("ep_pcie:%s: found driver for device 0x%x.",
				__func__, id);
			return present;
		}
	}

	pr_debug("ep_pcie:%s: driver for device 0x%x does not exist.",
			__func__, id);
	return NULL;
}
EXPORT_SYMBOL(ep_pcie_get_phandle);

int ep_pcie_register_event(struct ep_pcie_hw *phandle,
			struct ep_pcie_register_event *reg)
{
	if (phandle) {
		return phandle->register_event(reg);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_register_event);

int ep_pcie_deregister_event(struct ep_pcie_hw *phandle)
{
	if (phandle) {
		return phandle->deregister_event();
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_deregister_event);

enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle)
{
	if (phandle) {
		return phandle->get_linkstatus();
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_get_linkstatus);

int ep_pcie_config_outbound_iatu(struct ep_pcie_hw *phandle,
				struct ep_pcie_iatu entries[],
				u32 num_entries)
{
	if (phandle) {
		return phandle->config_outbound_iatu(entries, num_entries);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_config_outbound_iatu);

int ep_pcie_get_msi_config(struct ep_pcie_hw *phandle,
				struct ep_pcie_msi_config *cfg)
{
	if (phandle) {
		return phandle->get_msi_config(cfg);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_get_msi_config);

int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx)
{
	if (phandle) {
		return phandle->trigger_msi(idx);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_trigger_msi);

int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle)
{
	if (phandle) {
		return phandle->wakeup_host();
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_wakeup_host);

int ep_pcie_config_db_routing(struct ep_pcie_hw *phandle,
				struct ep_pcie_db_config chdb_cfg,
				struct ep_pcie_db_config erdb_cfg)
{
	if (phandle) {
		return phandle->config_db_routing(chdb_cfg, erdb_cfg);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_config_db_routing);

int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle,
				enum ep_pcie_options opt)
{
	if (phandle) {
		return phandle->enable_endpoint(opt);
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_enable_endpoint);

int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle)
{
	if (phandle) {
		return phandle->disable_endpoint();
	} else {
		pr_err("ep_pcie:%s: the input driver handle is NULL.",
			__func__);
		return -EINVAL;
	}
}
EXPORT_SYMBOL(ep_pcie_disable_endpoint);
Loading