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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJW/aaLAAoJEFKiBbHx2RXVn0gP/jF3xtTOwvWG53tULcj7y2b5
 9Zg0pvgONs84R0wRb7hABHaKm0IFCYVeoFSnA04Mio9GEATOB27QToVnqXyFypcc
 KHTncLLno6pcWY+JxoW7Im/+XMdnDEgdJ9KiH5zMkkkKME+82lefxW51GaA0WzUl
 ucjTAdjAO7D93S1qqSsLBq33i2XDpna2KaXhe7uJN71SrCTtPyieyAwhJH90VytY
 G3EPoZotrCeI1pJAoGNDQ6ZmdYJ1Vg2I29C2mUP/VJwOdoYkknzf12msRW+fdtAi
 3OnWPRhuwU2zOMzK23vgiElgu8x8FAPhVprHX/M8i/e+Xv7KlHKBC+Io0gF02l9e
 C7SaGNorIXiZmMAUCREAIRhzVkMf/bDbLD7vt80NSMkNzO4vwiN/KTrj/+TGpjVe
 Q448RN2b6puChMqbke+UIvYXutetkubzEAqSaaeGJagL5EhFMCXA71jqH+LcFS1c
 JGBEj1RNQ3N52jQbv6yiJDC9HEXUcQkLR5Yv2urpxiBXXoxUMhV39fRki/n/3xeb
 x3McwphEzyysI09xjhLWxYa46vp+zaE66/6pcbmvvfUqP4Y5ld1mWq5UB1bD31yf
 XChRDTblt3KGg3cIr8gO7Jcf3aBtJRxnnumy7Hej4qMSUiz6gNxmZkDiCvpktBhm
 IckP6Vm3Thf81KaLhGFh
 =wiIX
 -----END PGP SIGNATURE-----

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

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

* tag 'qcom-soc-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux:
  soc: qcom: smd: Support opening additional channels
  soc: qcom: smd: Support multiple channels per sdev
  soc: qcom: smd: Refactor channel open and close handling
  soc: qcom: smd: Split discovery and state change work
  soc: qcom: smd: Introduce callback setter
  drivers: qcom: spm: avoid module usage in non-modular SPM driver
  soc: qcom: smem_state: Add stubs for disabled smem_state
  MAINTAINERS: add qcom clocks to the maintainers list
  MAINTAINERS: add qcom i2c and spi drivers to list

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2016-04-13 10:56:40 -07:00
commit f6396838bd
5 changed files with 222 additions and 65 deletions

View File

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

View File

@ -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;
}
@ -792,6 +810,43 @@ static int qcom_smd_dev_match(struct device *dev, struct device_driver *drv)
return of_driver_match_device(dev, drv);
}
/*
* Helper for opening a channel
*/
static int qcom_smd_channel_open(struct qcom_smd_channel *channel,
qcom_smd_cb_t cb)
{
size_t bb_size;
/*
* Packets are maximum 4k, but reduce if the fifo is smaller
*/
bb_size = min(channel->fifo_size, SZ_4K);
channel->bounce_buffer = kmalloc(bb_size, GFP_KERNEL);
if (!channel->bounce_buffer)
return -ENOMEM;
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.
*
@ -803,22 +858,11 @@ 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;
size_t bb_size;
int ret;
/*
* Packets are maximum 4k, but reduce if the fifo is smaller
*/
bb_size = min(channel->fifo_size, SZ_4K);
channel->bounce_buffer = kmalloc(bb_size, GFP_KERNEL);
if (!channel->bounce_buffer)
return -ENOMEM;
channel->cb = qsdrv->callback;
qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING);
qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED);
ret = qcom_smd_channel_open(channel, qsdrv->callback);
if (ret)
return ret;
ret = qsdrv->probe(qsdev);
if (ret)
@ -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;

View File

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

View File

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

View File

@ -1,12 +1,17 @@
#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