Qualcomm ARM Based Driver Updates for v4.9

* Silence smem probe defer messages
 * Make scm explicitly non-modular
 * Assorted SMD bug fixes and minor changes
 * Add PM8018 RTC support
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXycjdAAoJEFKiBbHx2RXVVJAQAMZigw99xIh0vIlgQbquq3NF
 ih5HBow+yq+y744Zn9FJQvmPzGzPaoxTuvcvAh8M8qqoKpocolblEkcCgdgR62YK
 vKDHf8ZwTsR1t37jP35oUWE4xMNyHX5HFfDu267dwjzOpsFDYATotThTdp/T1muz
 uOWkmugrRGi04EfkVkX28uBEmvBmxDIAOrPclPifgYxpFlJ2d0/sC2SDtXwPR/Kk
 /Qb9aK659WUaqU2q0BmHKU2cgvo7oKa14vcriMgjU7jmzYKtvQrGmqt0YvCS320I
 XpYm4pDffG1HlOyz5a3KBakBzQRN24wQCXaSvVrY99B2/nLZtdVDnX29tw9mUioU
 GhfXanDMehgf4XJRwjhMsxiAYbSKEbaD5Rpr6rHebhPOVv8G4Yo36o4Q7yUr3Rpz
 pwFJdBMqA3S3roV318u0bgDjr4YjkYH7rRh1E8yjV7GFb9cMK0NPvfP8d/5l64Fk
 DE62Xl0eBmaHDd+gpousEAzZpIUp/TrTuGr0jhgLZbY+JMLHDlgiP8IZNx/EiZY4
 PMN2xUFaqKVPw+OzWg4Weg6OirInpzOfvjaxyb10yQIaDb+FYJGrDcZjjKsf4MPS
 vlKjzC9GbfPa/XI5iq+ULGtF6T6g8Nv29pUdXb75oH6vOhca8wWSAvhscP151N79
 /G13oF2ivpj+kvdLLfaX
 =yt6n
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Arnd Bergmann 2016-09-15 00:18:56 +02:00
commit fceec41b68
6 changed files with 192 additions and 128 deletions

View File

@ -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

View File

@ -1,4 +1,7 @@
/* 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");

View File

@ -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 },
{ },

View File

@ -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,18 +1369,121 @@ 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;
edge = kzalloc(sizeof(*edge), GFP_KERNEL);
if (!edge)
return ERR_PTR(-ENOMEM);
init_waitqueue_head(&edge->new_channel_event);
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);
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;
}
/**
* qcom_smd_unregister_edge() - release an edge and its children
* @edge: edge reference acquired from qcom_smd_register_edge
*/
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
int ret;
disable_irq(edge->irq);
cancel_work_sync(&edge->scan_work);
cancel_work_sync(&edge->state_work);
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);
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 */
@ -1419,59 +1491,32 @@ static int qcom_smd_probe(struct platform_device *pdev)
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;
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;
schedule_work(&edge->scan_work);
}
platform_set_drvdata(pdev, smd);
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)
{
struct qcom_smd_channel *channel;
struct qcom_smd_edge *edge;
struct qcom_smd *smd = platform_get_drvdata(pdev);
int i;
int ret;
for (i = 0; i < smd->num_edges; i++) {
edge = &smd->edges[i];
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);
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;
qcom_smd_destroy_device(channel);
}
}
return 0;
return ret;
}
static const struct of_device_id qcom_smd_of_match[] = {

View File

@ -740,7 +740,8 @@ 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) {
dev_err(&pdev->dev, "failed to retrieve hwlock\n");
if (hwlock_id != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to retrieve hwlock\n");
return hwlock_id;
}

View File

@ -55,11 +55,16 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel,
const char *name,
qcom_smd_cb_t cb);
void qcom_smd_close_channel(struct qcom_smd_channel *channel);
void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel);
void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
struct device_node *node);
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge);
#else
static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
@ -83,14 +88,20 @@ qcom_smd_open_channel(struct qcom_smd_channel *channel,
return NULL;
}
void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
static inline void qcom_smd_close_channel(struct qcom_smd_channel *channel)
{
/* This shouldn't be possible */
WARN_ON(1);
}
static inline void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
{
/* This shouldn't be possible */
WARN_ON(1);
return NULL;
}
void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
static inline void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
{
/* This shouldn't be possible */
WARN_ON(1);
@ -104,6 +115,20 @@ static inline int qcom_smd_send(struct qcom_smd_channel *channel,
return -ENXIO;
}
static inline struct qcom_smd_edge *
qcom_smd_register_edge(struct device *parent,
struct device_node *node)
{
return ERR_PTR(-ENXIO);
}
static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
/* This shouldn't be possible */
WARN_ON(1);
return -ENXIO;
}
#endif
#define module_qcom_smd_driver(__smd_driver) \