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

Commit b55b592e authored by Andy Gross's avatar Andy Gross
Browse files

Merge tag 'qcom-soc-for-4.7' into soc-for-4.7-p2

Qualcomm ARM Based SoC Updates for v4.7

* Add stubs for smem_state to fix build issues
* Fix module usage in SPM driver
* Add i2c and spi entries into QCOM MAINTAINERS entry
* Add SMD multi channel support
* Add clks to QCOM MAINTAINERS
parents f55532a0 028021d2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1470,7 +1470,10 @@ F: arch/arm/boot/dts/qcom-*.dts
F:	arch/arm/boot/dts/qcom-*.dtsi
F:	arch/arm/mach-qcom/
F:	arch/arm64/boot/dts/qcom/*
F:	drivers/i2c/busses/i2c-qup.c
F:	drivers/clk/qcom/
F:	drivers/soc/qcom/
F:	drivers/spi/spi-qup.c
F:	drivers/tty/serial/msm_serial.h
F:	drivers/tty/serial/msm_serial.c
F:	drivers/*/pm8???-*
+172 −57
Original line number Diff line number Diff line
@@ -106,9 +106,9 @@ static const struct {
 * @channels:		list of all channels detected on this edge
 * @channels_lock:	guard for modifications of @channels
 * @allocated:		array of bitmaps representing already allocated channels
 * @need_rescan:	flag that the @work needs to scan smem for new channels
 * @smem_available:	last available amount of smem triggering a channel scan
 * @work:		work item for edge house keeping
 * @scan_work:		work item for discovering new channels
 * @state_work:		work item for edge state changes
 */
struct qcom_smd_edge {
	struct qcom_smd *smd;
@@ -127,10 +127,12 @@ struct qcom_smd_edge {

	DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE);

	bool need_rescan;
	unsigned smem_available;

	struct work_struct work;
	wait_queue_head_t new_channel_event;

	struct work_struct scan_work;
	struct work_struct state_work;
};

/*
@@ -186,13 +188,14 @@ struct qcom_smd_channel {
	int fifo_size;

	void *bounce_buffer;
	int (*cb)(struct qcom_smd_device *, const void *, size_t);
	qcom_smd_cb_t cb;

	spinlock_t recv_lock;

	int pkt_size;

	struct list_head list;
	struct list_head dev_list;
};

/**
@@ -377,6 +380,19 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel)
	channel->pkt_size = 0;
}

/*
 * Set the callback for a channel, with appropriate locking
 */
static void qcom_smd_channel_set_callback(struct qcom_smd_channel *channel,
					  qcom_smd_cb_t cb)
{
	unsigned long flags;

	spin_lock_irqsave(&channel->recv_lock, flags);
	channel->cb = cb;
	spin_unlock_irqrestore(&channel->recv_lock, flags);
};

/*
 * Calculate the amount of data available in the rx fifo
 */
@@ -601,7 +617,8 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
	struct qcom_smd_edge *edge = data;
	struct qcom_smd_channel *channel;
	unsigned available;
	bool kick_worker = false;
	bool kick_scanner = false;
	bool kick_state = false;

	/*
	 * Handle state changes or data on each of the channels on this edge
@@ -609,7 +626,7 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
	spin_lock(&edge->channels_lock);
	list_for_each_entry(channel, &edge->channels, list) {
		spin_lock(&channel->recv_lock);
		kick_worker |= qcom_smd_channel_intr(channel);
		kick_state |= qcom_smd_channel_intr(channel);
		spin_unlock(&channel->recv_lock);
	}
	spin_unlock(&edge->channels_lock);
@@ -622,12 +639,13 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data)
	available = qcom_smem_get_free_space(edge->remote_pid);
	if (available != edge->smem_available) {
		edge->smem_available = available;
		edge->need_rescan = true;
		kick_worker = true;
		kick_scanner = true;
	}

	if (kick_worker)
		schedule_work(&edge->work);
	if (kick_scanner)
		schedule_work(&edge->scan_work);
	if (kick_state)
		schedule_work(&edge->state_work);

	return IRQ_HANDLED;
}
@@ -793,18 +811,12 @@ static int qcom_smd_dev_match(struct device *dev, struct device_driver *drv)
}

/*
 * Probe the smd client.
 *
 * The remote side have indicated that it want the channel to be opened, so
 * complete the state handshake and probe our client driver.
 * Helper for opening a channel
 */
static int qcom_smd_dev_probe(struct device *dev)
static int qcom_smd_channel_open(struct qcom_smd_channel *channel,
				 qcom_smd_cb_t cb)
{
	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;
	size_t bb_size;
	int ret;

	/*
	 * Packets are maximum 4k, but reduce if the fifo is smaller
@@ -814,12 +826,44 @@ static int qcom_smd_dev_probe(struct device *dev)
	if (!channel->bounce_buffer)
		return -ENOMEM;

	channel->cb = qsdrv->callback;

	qcom_smd_channel_set_callback(channel, cb);
	qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING);

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED);

	return 0;
}

/*
 * Helper for closing and resetting a channel
 */
static void qcom_smd_channel_close(struct qcom_smd_channel *channel)
{
	qcom_smd_channel_set_callback(channel, NULL);

	kfree(channel->bounce_buffer);
	channel->bounce_buffer = NULL;

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);
	qcom_smd_channel_reset(channel);
}

/*
 * Probe the smd client.
 *
 * The remote side have indicated that it want the channel to be opened, so
 * complete the state handshake and probe our client driver.
 */
static int qcom_smd_dev_probe(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;
	int ret;

	ret = qcom_smd_channel_open(channel, qsdrv->callback);
	if (ret)
		return ret;

	ret = qsdrv->probe(qsdev);
	if (ret)
		goto err;
@@ -831,11 +875,7 @@ static int qcom_smd_dev_probe(struct device *dev)
err:
	dev_err(&qsdev->dev, "probe failed\n");

	channel->cb = NULL;
	kfree(channel->bounce_buffer);
	channel->bounce_buffer = NULL;

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);
	qcom_smd_channel_close(channel);
	return ret;
}

@@ -850,16 +890,15 @@ 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;
	unsigned long flags;
	struct qcom_smd_channel *tmp;
	struct qcom_smd_channel *ch;

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING);

	/*
	 * Make sure we don't race with the code receiving data.
	 */
	spin_lock_irqsave(&channel->recv_lock, flags);
	channel->cb = NULL;
	spin_unlock_irqrestore(&channel->recv_lock, flags);
	qcom_smd_channel_set_callback(channel, NULL);

	/* Wake up any sleepers in qcom_smd_send() */
	wake_up_interruptible(&channel->fblockread_event);
@@ -872,15 +911,14 @@ static int qcom_smd_dev_remove(struct device *dev)
		qsdrv->remove(qsdev);

	/*
	 * The client is now gone, cleanup and reset the channel state.
	 * The client is now gone, close and release all channels associated
	 * with this sdev
	 */
	channel->qsdev = NULL;
	kfree(channel->bounce_buffer);
	channel->bounce_buffer = NULL;

	qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED);

	qcom_smd_channel_reset(channel);
	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;
	}

	return 0;
}
@@ -1006,6 +1044,77 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *qsdrv)
}
EXPORT_SYMBOL(qcom_smd_driver_unregister);

static struct qcom_smd_channel *
qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd_channel *ret = NULL;
	unsigned long flags;
	unsigned state;

	spin_lock_irqsave(&edge->channels_lock, flags);
	list_for_each_entry(channel, &edge->channels, list) {
		if (strcmp(channel->name, name))
			continue;

		state = GET_RX_CHANNEL_INFO(channel, state);
		if (state != SMD_CHANNEL_OPENING &&
		    state != SMD_CHANNEL_OPENED)
			continue;

		ret = channel;
		break;
	}
	spin_unlock_irqrestore(&edge->channels_lock, flags);

	return ret;
}

/**
 * qcom_smd_open_channel() - claim additional channels on the same edge
 * @sdev:	smd_device handle
 * @name:	channel name
 * @cb:		callback method to use for incoming data
 *
 * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
 * ready.
 */
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev,
					       const char *name,
					       qcom_smd_cb_t cb)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd_edge *edge = sdev->channel->edge;
	int ret;

	/* Wait up to HZ for the channel to appear */
	ret = wait_event_interruptible_timeout(edge->new_channel_event,
			(channel = qcom_smd_find_channel(edge, name)) != NULL,
			HZ);
	if (!ret)
		return ERR_PTR(-ETIMEDOUT);

	if (channel->state != SMD_CHANNEL_CLOSED) {
		dev_err(&sdev->dev, "channel %s is busy\n", channel->name);
		return ERR_PTR(-EBUSY);
	}

	channel->qsdev = sdev;
	ret = qcom_smd_channel_open(channel, cb);
	if (ret) {
		channel->qsdev = NULL;
		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);

/*
 * Allocate the qcom_smd_channel object for a newly found smd channel,
 * retrieving and validating the smem items involved.
@@ -1027,6 +1136,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
	if (!channel)
		return ERR_PTR(-ENOMEM);

	INIT_LIST_HEAD(&channel->dev_list);
	channel->edge = edge;
	channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL);
	if (!channel->name)
@@ -1089,8 +1199,9 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
 * qcom_smd_create_channel() to create representations of these and add
 * them to the edge's list of channels.
 */
static void qcom_discover_channels(struct qcom_smd_edge *edge)
static void qcom_channel_scan_worker(struct work_struct *work)
{
	struct qcom_smd_edge *edge = container_of(work, struct qcom_smd_edge, scan_work);
	struct qcom_smd_alloc_entry *alloc_tbl;
	struct qcom_smd_alloc_entry *entry;
	struct qcom_smd_channel *channel;
@@ -1140,10 +1251,12 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)

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

			wake_up_interruptible(&edge->new_channel_event);
		}
	}

	schedule_work(&edge->work);
	schedule_work(&edge->state_work);
}

/*
@@ -1151,29 +1264,23 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)
 * then scans all registered channels for state changes that should be handled
 * by creating or destroying smd client devices for the registered channels.
 *
 * LOCKING: edge->channels_lock is not needed to be held during the traversal
 * of the channels list as it's done synchronously with the only writer.
 * LOCKING: edge->channels_lock only needs to cover the list operations, as the
 * worker is killed before any channels are deallocated
 */
static void qcom_channel_state_worker(struct work_struct *work)
{
	struct qcom_smd_channel *channel;
	struct qcom_smd_edge *edge = container_of(work,
						  struct qcom_smd_edge,
						  work);
						  state_work);
	unsigned remote_state;

	/*
	 * Rescan smem if we have reason to belive that there are new channels.
	 */
	if (edge->need_rescan) {
		edge->need_rescan = false;
		qcom_discover_channels(edge);
	}
	unsigned long flags;

	/*
	 * Register a device for any closed channel where the remote processor
	 * is showing interest in opening the channel.
	 */
	spin_lock_irqsave(&edge->channels_lock, flags);
	list_for_each_entry(channel, &edge->channels, list) {
		if (channel->state != SMD_CHANNEL_CLOSED)
			continue;
@@ -1183,7 +1290,9 @@ static void qcom_channel_state_worker(struct work_struct *work)
		    remote_state != SMD_CHANNEL_OPENED)
			continue;

		spin_unlock_irqrestore(&edge->channels_lock, flags);
		qcom_smd_create_device(channel);
		spin_lock_irqsave(&edge->channels_lock, flags);
	}

	/*
@@ -1200,8 +1309,11 @@ static void qcom_channel_state_worker(struct work_struct *work)
		    remote_state == SMD_CHANNEL_OPENED)
			continue;

		spin_unlock_irqrestore(&edge->channels_lock, flags);
		qcom_smd_destroy_device(channel);
		spin_lock_irqsave(&edge->channels_lock, flags);
	}
	spin_unlock_irqrestore(&edge->channels_lock, flags);
}

/*
@@ -1219,7 +1331,8 @@ static int qcom_smd_parse_edge(struct device *dev,
	INIT_LIST_HEAD(&edge->channels);
	spin_lock_init(&edge->channels_lock);

	INIT_WORK(&edge->work, qcom_channel_state_worker);
	INIT_WORK(&edge->scan_work, qcom_channel_scan_worker);
	INIT_WORK(&edge->state_work, qcom_channel_state_worker);

	edge->of_node = of_node_get(node);

@@ -1303,13 +1416,13 @@ static int qcom_smd_probe(struct platform_device *pdev)
	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->need_rescan = true;
		schedule_work(&edge->work);
		schedule_work(&edge->scan_work);
	}

	platform_set_drvdata(pdev, smd);
@@ -1332,8 +1445,10 @@ static int qcom_smd_remove(struct platform_device *pdev)
		edge = &smd->edges[i];

		disable_irq(edge->irq);
		cancel_work_sync(&edge->work);
		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;
+3 −5
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@
 * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 * Copyright (c) 2014,2015, Linaro Ltd.
 *
 * SAW power controller driver
 *
 * 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.
@@ -12,7 +14,6 @@
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -378,8 +379,5 @@ static struct platform_driver spm_driver = {
		.of_match_table = spm_match_table,
	},
};
module_platform_driver(spm_driver);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SAW power controller driver");
MODULE_ALIAS("platform:saw");
builtin_platform_driver(spm_driver);
+7 −1
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ struct qcom_smd_device {
	struct qcom_smd_channel *channel;
};

typedef int (*qcom_smd_cb_t)(struct qcom_smd_device *, const void *, size_t);

/**
 * struct qcom_smd_driver - smd driver struct
 * @driver:	underlying device driver
@@ -42,7 +44,7 @@ struct qcom_smd_driver {

	int (*probe)(struct qcom_smd_device *dev);
	void (*remove)(struct qcom_smd_device *dev);
	int (*callback)(struct qcom_smd_device *, const void *, size_t);
	qcom_smd_cb_t callback;
};

int qcom_smd_driver_register(struct qcom_smd_driver *drv);
@@ -54,4 +56,8 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);

int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);

struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_device *sdev,
					       const char *name,
					       qcom_smd_cb_t cb);

#endif
+35 −0
Original line number Diff line number Diff line
#ifndef __QCOM_SMEM_STATE__
#define __QCOM_SMEM_STATE__

#include <linux/errno.h>

struct device_node;
struct qcom_smem_state;

struct qcom_smem_state_ops {
	int (*update_bits)(void *, u32, u32);
};

#ifdef CONFIG_QCOM_SMEM_STATE

struct qcom_smem_state *qcom_smem_state_get(struct device *dev, const char *con_id, unsigned *bit);
void qcom_smem_state_put(struct qcom_smem_state *);

@@ -15,4 +20,34 @@ int qcom_smem_state_update_bits(struct qcom_smem_state *state, u32 mask, u32 val
struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, const struct qcom_smem_state_ops *ops, void *data);
void qcom_smem_state_unregister(struct qcom_smem_state *state);

#else

static inline struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
	const char *con_id, unsigned *bit)
{
	return ERR_PTR(-EINVAL);
}

static inline void qcom_smem_state_put(struct qcom_smem_state *state)
{
}

static inline int qcom_smem_state_update_bits(struct qcom_smem_state *state,
	u32 mask, u32 value)
{
	return -EINVAL;
}

static inline struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
	const struct qcom_smem_state_ops *ops, void *data)
{
	return ERR_PTR(-EINVAL);
}

static inline void qcom_smem_state_unregister(struct qcom_smem_state *state)
{
}

#endif

#endif