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

Commit fceec41b authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'qcom-drivers-for-4.9' of...

Merge tag 'qcom-drivers-for-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux into next/drivers

Pull "Qualcomm ARM Based Driver Updates for v4.9" from Andy Gross:

* Silence smem probe defer messages
* Make scm explicitly non-modular
* Assorted SMD bug fixes and minor changes
* Add PM8018 RTC support

* tag 'qcom-drivers-for-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux:
  rtc: rtc-pm8xxx: Add support for pm8018 rtc
  soc: qcom: smd: Reset rx tail rather than tx
  soc: qcom: smd: Represent smd edges as devices
  soc: qcom: smd: Request irqs after parsing properties
  soc: qcom: smd: Simplify multi channel handling
  soc: qcom: smd: Correct compile stub prototypes
  firmware: qcom_scm: make it explicitly non-modular
  soc: qcom: smem: Silence probe defer error
parents e747ea14 23c282d6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ The below bindings specify the set of valid subnodes.
		    "qcom,pm8058-rtc"
		    "qcom,pm8921-rtc"
		    "qcom,pm8941-rtc"
		    "qcom,pm8018-rtc"

- reg:
	Usage: required
+5 −14
Original line number Diff line number Diff line
/* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
/*
 * Qualcomm SCM driver
 *
 * Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
 * Copyright (C) 2015 Linaro Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
@@ -12,7 +15,7 @@
 *
 */
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/dma-mapping.h>
@@ -376,8 +379,6 @@ static const struct of_device_id qcom_scm_dt_match[] = {
	{}
};

MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);

static struct platform_driver qcom_scm_driver = {
	.driver = {
		.name	= "qcom_scm",
@@ -414,14 +415,4 @@ static int __init qcom_scm_init(void)

	return platform_driver_register(&qcom_scm_driver);
}

subsys_initcall(qcom_scm_init);

static void __exit qcom_scm_exit(void)
{
	platform_driver_unregister(&qcom_scm_driver);
}
module_exit(qcom_scm_exit);

MODULE_DESCRIPTION("Qualcomm SCM driver");
MODULE_LICENSE("GPL v2");
+1 −0
Original line number Diff line number Diff line
@@ -428,6 +428,7 @@ static const struct pm8xxx_rtc_regs pm8941_regs = {
 */
static const struct of_device_id pm8xxx_id_table[] = {
	{ .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
	{ .compatible = "qcom,pm8018-rtc", .data = &pm8921_regs },
	{ .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
	{ .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
	{ },
+155 −110
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ static const struct {

/**
 * struct qcom_smd_edge - representing a remote processor
 * @smd:		handle to qcom_smd
 * @dev:		device for this edge
 * @of_node:		of_node handle for information related to this edge
 * @edge_id:		identifier of this edge
 * @remote_pid:		identifier of remote processor
@@ -111,7 +111,8 @@ static const struct {
 * @state_work:		work item for edge state changes
 */
struct qcom_smd_edge {
	struct qcom_smd *smd;
	struct device dev;

	struct device_node *of_node;
	unsigned edge_id;
	unsigned remote_pid;
@@ -135,6 +136,8 @@ struct qcom_smd_edge {
	struct work_struct state_work;
};

#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)

/*
 * SMD channel states.
 */
@@ -197,20 +200,6 @@ struct qcom_smd_channel {
	void *drvdata;

	struct list_head list;
	struct list_head dev_list;
};

/**
 * struct qcom_smd - smd struct
 * @dev:	device struct
 * @num_edges:	number of entries in @edges
 * @edges:	array of edges to be handled
 */
struct qcom_smd {
	struct device *dev;

	unsigned num_edges;
	struct qcom_smd_edge edges[0];
};

/*
@@ -374,7 +363,7 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel)
	SET_TX_CHANNEL_FLAG(channel, fSTATE, 1);
	SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
	SET_TX_CHANNEL_INFO(channel, head, 0);
	SET_TX_CHANNEL_INFO(channel, tail, 0);
	SET_RX_CHANNEL_INFO(channel, tail, 0);

	qcom_smd_signal_channel(channel);

@@ -421,7 +410,7 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
	if (channel->state == state)
		return;

	dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state);
	dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state);

	SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
	SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
@@ -891,8 +880,6 @@ static int qcom_smd_dev_remove(struct device *dev)
	struct qcom_smd_device *qsdev = to_smd_device(dev);
	struct qcom_smd_driver *qsdrv = to_smd_driver(dev);
	struct qcom_smd_channel *channel = qsdev->channel;
	struct qcom_smd_channel *tmp;
	struct qcom_smd_channel *ch;

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING);

@@ -911,15 +898,9 @@ static int qcom_smd_dev_remove(struct device *dev)
	if (qsdrv->remove)
		qsdrv->remove(qsdev);

	/*
	 * The client is now gone, close and release all channels associated
	 * with this sdev
	 */
	list_for_each_entry_safe(ch, tmp, &channel->dev_list, dev_list) {
		qcom_smd_channel_close(ch);
		list_del(&ch->dev_list);
		ch->qsdev = NULL;
	}
	/* The client is now gone, close the primary channel */
	qcom_smd_channel_close(channel);
	channel->qsdev = NULL;

	return 0;
}
@@ -973,13 +954,12 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
	struct qcom_smd_device *qsdev;
	struct qcom_smd_edge *edge = channel->edge;
	struct device_node *node;
	struct qcom_smd *smd = edge->smd;
	int ret;

	if (channel->qsdev)
		return -EEXIST;

	dev_dbg(smd->dev, "registering '%s'\n", channel->name);
	dev_dbg(&edge->dev, "registering '%s'\n", channel->name);

	qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
	if (!qsdev)
@@ -990,7 +970,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
		     edge->of_node->name,
		     node ? node->name : channel->name);

	qsdev->dev.parent = smd->dev;
	qsdev->dev.parent = &edge->dev;
	qsdev->dev.bus = &qcom_smd_bus;
	qsdev->dev.release = qcom_smd_release_device;
	qsdev->dev.of_node = node;
@@ -1001,7 +981,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)

	ret = device_register(&qsdev->dev);
	if (ret) {
		dev_err(smd->dev, "device_register failed: %d\n", ret);
		dev_err(&edge->dev, "device_register failed: %d\n", ret);
		put_device(&qsdev->dev);
	}

@@ -1091,6 +1071,8 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name)
 *
 * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
 * ready.
 *
 * Any channels returned must be closed with a call to qcom_smd_close_channel()
 */
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
					       const char *name,
@@ -1120,15 +1102,21 @@ struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
		return ERR_PTR(ret);
	}

	/*
	 * Append the list of channel to the channels associated with the sdev
	 */
	list_add_tail(&channel->dev_list, &sdev->channel->dev_list);

	return channel;
}
EXPORT_SYMBOL(qcom_smd_open_channel);

/**
 * qcom_smd_close_channel() - close an additionally opened channel
 * @channel:	channel handle, returned by qcom_smd_open_channel()
 */
void qcom_smd_close_channel(struct qcom_smd_channel *channel)
{
	qcom_smd_channel_close(channel);
	channel->qsdev = NULL;
}
EXPORT_SYMBOL(qcom_smd_close_channel);

/*
 * Allocate the qcom_smd_channel object for a newly found smd channel,
 * retrieving and validating the smem items involved.
@@ -1139,20 +1127,18 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
							char *name)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd *smd = edge->smd;
	size_t fifo_size;
	size_t info_size;
	void *fifo_base;
	void *info;
	int ret;

	channel = devm_kzalloc(smd->dev, sizeof(*channel), GFP_KERNEL);
	channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL);
	if (!channel)
		return ERR_PTR(-ENOMEM);

	INIT_LIST_HEAD(&channel->dev_list);
	channel->edge = edge;
	channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL);
	channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL);
	if (!channel->name)
		return ERR_PTR(-ENOMEM);

@@ -1175,7 +1161,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
	} else if (info_size == 2 * sizeof(struct smd_channel_info)) {
		channel->info = info;
	} else {
		dev_err(smd->dev,
		dev_err(&edge->dev,
			"channel info of size %zu not supported\n", info_size);
		ret = -EINVAL;
		goto free_name_and_channel;
@@ -1190,7 +1176,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
	/* The channel consist of a rx and tx fifo of equal size */
	fifo_size /= 2;

	dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
	dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
			  name, info_size, fifo_size);

	channel->tx_fifo = fifo_base;
@@ -1202,8 +1188,8 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
	return channel;

free_name_and_channel:
	devm_kfree(smd->dev, channel->name);
	devm_kfree(smd->dev, channel);
	devm_kfree(&edge->dev, channel->name);
	devm_kfree(&edge->dev, channel);

	return ERR_PTR(ret);
}
@@ -1219,7 +1205,6 @@ static void qcom_channel_scan_worker(struct work_struct *work)
	struct qcom_smd_alloc_entry *alloc_tbl;
	struct qcom_smd_alloc_entry *entry;
	struct qcom_smd_channel *channel;
	struct qcom_smd *smd = edge->smd;
	unsigned long flags;
	unsigned fifo_id;
	unsigned info_id;
@@ -1263,7 +1248,7 @@ static void qcom_channel_scan_worker(struct work_struct *work)
			list_add(&channel->list, &edge->channels);
			spin_unlock_irqrestore(&edge->channels_lock, flags);

			dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name);
			dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name);
			set_bit(i, edge->allocated[tbl]);

			wake_up_interruptible(&edge->new_channel_event);
@@ -1350,22 +1335,6 @@ static int qcom_smd_parse_edge(struct device *dev,

	edge->of_node = of_node_get(node);

	irq = irq_of_parse_and_map(node, 0);
	if (irq < 0) {
		dev_err(dev, "required smd interrupt missing\n");
		return -EINVAL;
	}

	ret = devm_request_irq(dev, irq,
			       qcom_smd_edge_intr, IRQF_TRIGGER_RISING,
			       node->name, edge);
	if (ret) {
		dev_err(dev, "failed to request smd irq\n");
		return ret;
	}

	edge->irq = irq;

	key = "qcom,smd-edge";
	ret = of_property_read_u32(node, key, &edge->edge_id);
	if (ret) {
@@ -1400,80 +1369,156 @@ static int qcom_smd_parse_edge(struct device *dev,
		return -EINVAL;
	}

	irq = irq_of_parse_and_map(node, 0);
	if (irq < 0) {
		dev_err(dev, "required smd interrupt missing\n");
		return -EINVAL;
	}

	ret = devm_request_irq(dev, irq,
			       qcom_smd_edge_intr, IRQF_TRIGGER_RISING,
			       node->name, edge);
	if (ret) {
		dev_err(dev, "failed to request smd irq\n");
		return ret;
	}

	edge->irq = irq;

	return 0;
}

static int qcom_smd_probe(struct platform_device *pdev)
/*
 * Release function for an edge.
 * Reset the state of each associated channel and free the edge context.
 */
static void qcom_smd_edge_release(struct device *dev)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd_edge *edge = to_smd_edge(dev);

	list_for_each_entry(channel, &edge->channels, list) {
		SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED);
		SET_RX_CHANNEL_INFO(channel, head, 0);
		SET_RX_CHANNEL_INFO(channel, tail, 0);
	}

	kfree(edge);
}

/**
 * qcom_smd_register_edge() - register an edge based on an device_node
 * @parent:	parent device for the edge
 * @node:	device_node describing the edge
 *
 * Returns an edge reference, or negative ERR_PTR() on failure.
 */
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
					     struct device_node *node)
{
	struct qcom_smd_edge *edge;
	struct device_node *node;
	struct qcom_smd *smd;
	size_t array_size;
	int num_edges;
	int ret;
	int i = 0;
	void *p;

	/* Wait for smem */
	p = qcom_smem_get(QCOM_SMEM_HOST_ANY, smem_items[0].alloc_tbl_id, NULL);
	if (PTR_ERR(p) == -EPROBE_DEFER)
		return PTR_ERR(p);

	num_edges = of_get_available_child_count(pdev->dev.of_node);
	array_size = sizeof(*smd) + num_edges * sizeof(struct qcom_smd_edge);
	smd = devm_kzalloc(&pdev->dev, array_size, GFP_KERNEL);
	if (!smd)
		return -ENOMEM;
	smd->dev = &pdev->dev;
	edge = kzalloc(sizeof(*edge), GFP_KERNEL);
	if (!edge)
		return ERR_PTR(-ENOMEM);

	smd->num_edges = num_edges;
	for_each_available_child_of_node(pdev->dev.of_node, node) {
		edge = &smd->edges[i++];
		edge->smd = smd;
	init_waitqueue_head(&edge->new_channel_event);

		ret = qcom_smd_parse_edge(&pdev->dev, node, edge);
		if (ret)
			continue;
	edge->dev.parent = parent;
	edge->dev.release = qcom_smd_edge_release;
	dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
	ret = device_register(&edge->dev);
	if (ret) {
		pr_err("failed to register smd edge\n");
		return ERR_PTR(ret);
	}

	ret = qcom_smd_parse_edge(&edge->dev, node, edge);
	if (ret) {
		dev_err(&edge->dev, "failed to parse smd edge\n");
		goto unregister_dev;
	}

	schedule_work(&edge->scan_work);

	return edge;

unregister_dev:
	put_device(&edge->dev);
	return ERR_PTR(ret);
}
EXPORT_SYMBOL(qcom_smd_register_edge);

	platform_set_drvdata(pdev, smd);
static int qcom_smd_remove_device(struct device *dev, void *data)
{
	device_unregister(dev);
	of_node_put(dev->of_node);
	put_device(dev);

	return 0;
}

/*
 * Shut down all smd clients by making sure that each edge stops processing
 * events and scanning for new channels, then call destroy on the devices.
/**
 * qcom_smd_unregister_edge() - release an edge and its children
 * @edge:	edge reference acquired from qcom_smd_register_edge
 */
static int qcom_smd_remove(struct platform_device *pdev)
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd_edge *edge;
	struct qcom_smd *smd = platform_get_drvdata(pdev);
	int i;

	for (i = 0; i < smd->num_edges; i++) {
		edge = &smd->edges[i];
	int ret;

	disable_irq(edge->irq);
	cancel_work_sync(&edge->scan_work);
	cancel_work_sync(&edge->state_work);

		/* No need to lock here, because the writer is gone */
		list_for_each_entry(channel, &edge->channels, list) {
			if (!channel->qsdev)
				continue;
	ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device);
	if (ret)
		dev_warn(&edge->dev, "can't remove smd device: %d\n", ret);

			qcom_smd_destroy_device(channel);
		}
	device_unregister(&edge->dev);

	return 0;
}
EXPORT_SYMBOL(qcom_smd_unregister_edge);

static int qcom_smd_probe(struct platform_device *pdev)
{
	struct device_node *node;
	void *p;

	/* Wait for smem */
	p = qcom_smem_get(QCOM_SMEM_HOST_ANY, smem_items[0].alloc_tbl_id, NULL);
	if (PTR_ERR(p) == -EPROBE_DEFER)
		return PTR_ERR(p);

	for_each_available_child_of_node(pdev->dev.of_node, node)
		qcom_smd_register_edge(&pdev->dev, node);

	return 0;
}

static int qcom_smd_remove_edge(struct device *dev, void *data)
{
	struct qcom_smd_edge *edge = to_smd_edge(dev);

	return qcom_smd_unregister_edge(edge);
}

/*
 * Shut down all smd clients by making sure that each edge stops processing
 * events and scanning for new channels, then call destroy on the devices.
 */
static int qcom_smd_remove(struct platform_device *pdev)
{
	int ret;

	ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge);
	if (ret)
		dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret);

	return ret;
}

static const struct of_device_id qcom_smd_of_match[] = {
	{ .compatible = "qcom,smd" },
	{}
+2 −1
Original line number Diff line number Diff line
@@ -740,6 +740,7 @@ static int qcom_smem_probe(struct platform_device *pdev)

	hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
	if (hwlock_id < 0) {
		if (hwlock_id != -EPROBE_DEFER)
			dev_err(&pdev->dev, "failed to retrieve hwlock\n");
		return hwlock_id;
	}
Loading