Commit 47ec7e0b authored by Olof Johansson's avatar Olof Johansson

Merge tag 'qcom-soc-for-4.5' of...

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

Qualcomm ARM Based SoC Updates for 4.5

* Add WCNSS_CTRL client
* Various Kconfig changes to fix build issues
* Update SoC Qualcomm MAINTAINERS entry
* Add SMP2P, SMSM, and SMEM state machine drivers
* Add SMD-RPM support for existing platforms

* tag 'qcom-soc-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux:
  MAINTAINERS: Change QCOM entries
  soc: qcom: smd-rpm: Add existing platform support
  soc: qcom: Introduce WCNSS_CTRL SMD client
  ARM: qcom: select ARM_CPU_SUSPEND for power management
  MAINTAINERS: Add rules for Qualcomm dts files
  soc: qcom: enable smsm/smp2p modular build
  serial: msm_serial: Make config tristate
  soc: qcom: smp2p: Qualcomm Shared Memory Point to Point
  soc: qcom: smsm: Add driver for Qualcomm SMSM
  soc: qcom: Introduce common SMEM state machine code
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 8483ff07 bbeaa595
......@@ -1401,11 +1401,13 @@ S: Maintained
ARM/QUALCOMM SUPPORT
M: Kumar Gala <galak@codeaurora.org>
M: Andy Gross <agross@codeaurora.org>
M: David Brown <davidb@codeaurora.org>
M: Andy Gross <andy.gross@linaro.org>
M: David Brown <david.brown@linaro.org>
L: linux-arm-msm@vger.kernel.org
L: linux-soc@vger.kernel.org
S: Maintained
F: arch/arm/boot/dts/qcom-*.dts
F: arch/arm/boot/dts/qcom-*.dtsi
F: arch/arm/mach-qcom/
F: drivers/soc/qcom/
F: drivers/tty/serial/msm_serial.h
......@@ -1413,7 +1415,7 @@ F: drivers/tty/serial/msm_serial.c
F: drivers/*/pm8???-*
F: drivers/mfd/ssbi.c
F: drivers/firmware/qcom_scm.c
T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux.git
ARM/RADISYS ENP2611 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
......
......@@ -13,6 +13,7 @@ config QCOM_GSBI
config QCOM_PM
bool "Qualcomm Power Management"
depends on ARCH_QCOM && !ARM64
select ARM_CPU_SUSPEND
select QCOM_SCM
help
QCOM Platform specific power driver to manage cores and L2 low power
......@@ -49,3 +50,29 @@ config QCOM_SMD_RPM
Say M here if you want to include support for the Qualcomm RPM as a
module. This will build a module called "qcom-smd-rpm".
config QCOM_SMEM_STATE
bool
config QCOM_SMP2P
tristate "Qualcomm Shared Memory Point to Point support"
depends on QCOM_SMEM
select QCOM_SMEM_STATE
help
Say yes here to support the Qualcomm Shared Memory Point to Point
protocol.
config QCOM_SMSM
tristate "Qualcomm Shared Memory State Machine"
depends on QCOM_SMEM
select QCOM_SMEM_STATE
help
Say yes here to support the Qualcomm Shared Memory State Machine.
The state machine is represented by bits in shared memory.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on QCOM_SMD
help
Client driver for the WCNSS_CTRL SMD channel, used to download nv
firmware to a newly booted WCNSS chip.
......@@ -3,3 +3,7 @@ obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
obj-$(CONFIG_QCOM_SMEM) += smem.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
......@@ -219,6 +219,8 @@ static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev)
}
static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-apq8084" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8974" },
{}
};
......
/*
* Copyright (c) 2015, Sony Mobile Communications Inc.
* Copyright (c) 2012-2013, 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.
*/
#include <linux/device.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem_state.h>
static LIST_HEAD(smem_states);
static DEFINE_MUTEX(list_lock);
/**
* struct qcom_smem_state - state context
* @refcount: refcount for the state
* @orphan: boolean indicator that this state has been unregistered
* @list: entry in smem_states list
* @of_node: of_node to use for matching the state in DT
* @priv: implementation private data
* @ops: ops for the state
*/
struct qcom_smem_state {
struct kref refcount;
bool orphan;
struct list_head list;
struct device_node *of_node;
void *priv;
struct qcom_smem_state_ops ops;
};
/**
* qcom_smem_state_update_bits() - update the masked bits in state with value
* @state: state handle acquired by calling qcom_smem_state_get()
* @mask: bit mask for the change
* @value: new value for the masked bits
*
* Returns 0 on success, otherwise negative errno.
*/
int qcom_smem_state_update_bits(struct qcom_smem_state *state,
u32 mask,
u32 value)
{
if (state->orphan)
return -ENXIO;
if (!state->ops.update_bits)
return -ENOTSUPP;
return state->ops.update_bits(state->priv, mask, value);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
static struct qcom_smem_state *of_node_to_state(struct device_node *np)
{
struct qcom_smem_state *state;
mutex_lock(&list_lock);
list_for_each_entry(state, &smem_states, list) {
if (state->of_node == np) {
kref_get(&state->refcount);
goto unlock;
}
}
state = ERR_PTR(-EPROBE_DEFER);
unlock:
mutex_unlock(&list_lock);
return state;
}
/**
* qcom_smem_state_get() - acquire handle to a state
* @dev: client device pointer
* @con_id: name of the state to lookup
* @bit: flags from the state reference, indicating which bit's affected
*
* Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
* called to release the returned state handle.
*/
struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
const char *con_id,
unsigned *bit)
{
struct qcom_smem_state *state;
struct of_phandle_args args;
int index = 0;
int ret;
if (con_id) {
index = of_property_match_string(dev->of_node,
"qcom,state-names",
con_id);
if (index < 0) {
dev_err(dev, "missing qcom,state-names\n");
return ERR_PTR(index);
}
}
ret = of_parse_phandle_with_args(dev->of_node,
"qcom,state",
"#qcom,state-cells",
index,
&args);
if (ret) {
dev_err(dev, "failed to parse qcom,state property\n");
return ERR_PTR(ret);
}
if (args.args_count != 1) {
dev_err(dev, "invalid #qcom,state-cells\n");
return ERR_PTR(-EINVAL);
}
state = of_node_to_state(args.np);
if (IS_ERR(state))
goto put;
*bit = args.args[0];
put:
of_node_put(args.np);
return state;
}
EXPORT_SYMBOL_GPL(qcom_smem_state_get);
static void qcom_smem_state_release(struct kref *ref)
{
struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
list_del(&state->list);
kfree(state);
}
/**
* qcom_smem_state_put() - release state handle
* @state: state handle to be released
*/
void qcom_smem_state_put(struct qcom_smem_state *state)
{
mutex_lock(&list_lock);
kref_put(&state->refcount, qcom_smem_state_release);
mutex_unlock(&list_lock);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_put);
/**
* qcom_smem_state_register() - register a new state
* @of_node: of_node used for matching client lookups
* @ops: implementation ops
* @priv: implementation specific private data
*/
struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
const struct qcom_smem_state_ops *ops,
void *priv)
{
struct qcom_smem_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return ERR_PTR(-ENOMEM);
kref_init(&state->refcount);
state->of_node = of_node;
state->ops = *ops;
state->priv = priv;
mutex_lock(&list_lock);
list_add(&state->list, &smem_states);
mutex_unlock(&list_lock);
return state;
}
EXPORT_SYMBOL_GPL(qcom_smem_state_register);
/**
* qcom_smem_state_unregister() - unregister a registered state
* @state: state handle to be unregistered
*/
void qcom_smem_state_unregister(struct qcom_smem_state *state)
{
state->orphan = true;
qcom_smem_state_put(state);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* 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.
*/
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
#define NV_FRAGMENT_SIZE 3072
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
/**
* struct wcnss_ctrl - driver context
* @dev: device handle
* @channel: SMD channel handle
* @ack: completion for outstanding requests
* @ack_status: status of the outstanding request
* @download_nv_work: worker for uploading nv binary
*/
struct wcnss_ctrl {
struct device *dev;
struct qcom_smd_channel *channel;
struct completion ack;
int ack_status;
struct work_struct download_nv_work;
};
/* message types */
enum {
WCNSS_VERSION_REQ = 0x01000000,
WCNSS_VERSION_RESP,
WCNSS_DOWNLOAD_NV_REQ,
WCNSS_DOWNLOAD_NV_RESP,
WCNSS_UPLOAD_CAL_REQ,
WCNSS_UPLOAD_CAL_RESP,
WCNSS_DOWNLOAD_CAL_REQ,
WCNSS_DOWNLOAD_CAL_RESP,
};
/**
* struct wcnss_msg_hdr - common packet header for requests and responses
* @type: packet message type
* @len: total length of the packet, including this header
*/
struct wcnss_msg_hdr {
u32 type;
u32 len;
} __packed;
/**
* struct wcnss_version_resp - version request response
* @hdr: common packet wcnss_msg_hdr header
*/
struct wcnss_version_resp {
struct wcnss_msg_hdr hdr;
u8 major;
u8 minor;
u8 version;
u8 revision;
} __packed;
/**
* struct wcnss_download_nv_req - firmware fragment request
* @hdr: common packet wcnss_msg_hdr header
* @seq: sequence number of this fragment
* @last: boolean indicator of this being the last fragment of the binary
* @frag_size: length of this fragment
* @fragment: fragment data
*/
struct wcnss_download_nv_req {
struct wcnss_msg_hdr hdr;
u16 seq;
u16 last;
u32 frag_size;
u8 fragment[];
} __packed;
/**
* struct wcnss_download_nv_resp - firmware download response
* @hdr: common packet wcnss_msg_hdr header
* @status: boolean to indicate success of the download
*/
struct wcnss_download_nv_resp {
struct wcnss_msg_hdr hdr;
u8 status;
} __packed;
/**
* wcnss_ctrl_smd_callback() - handler from SMD responses
* @qsdev: smd device handle
* @data: pointer to the incoming data packet
* @count: size of the incoming data packet
*
* Handles any incoming packets from the remote WCNSS_CTRL service.
*/
static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev,
const void *data,
size_t count)
{
struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev);
const struct wcnss_download_nv_resp *nvresp;
const struct wcnss_version_resp *version;
const struct wcnss_msg_hdr *hdr = data;
switch (hdr->type) {
case WCNSS_VERSION_RESP:
if (count != sizeof(*version)) {
dev_err(wcnss->dev,
"invalid size of version response\n");
break;
}
version = data;
dev_info(wcnss->dev, "WCNSS Version %d.%d %d.%d\n",
version->major, version->minor,
version->version, version->revision);
schedule_work(&wcnss->download_nv_work);
break;
case WCNSS_DOWNLOAD_NV_RESP:
if (count != sizeof(*nvresp)) {
dev_err(wcnss->dev,
"invalid size of download response\n");
break;
}
nvresp = data;
wcnss->ack_status = nvresp->status;
complete(&wcnss->ack);
break;
default:
dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
break;
}
return 0;
}
/**
* wcnss_request_version() - send a version request to WCNSS
* @wcnss: wcnss ctrl driver context
*/
static int wcnss_request_version(struct wcnss_ctrl *wcnss)
{
struct wcnss_msg_hdr msg;
msg.type = WCNSS_VERSION_REQ;
msg.len = sizeof(msg);
return qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
}
/**
* wcnss_download_nv() - send nv binary to WCNSS
* @work: work struct to acquire wcnss context
*/
static void wcnss_download_nv(struct work_struct *work)
{
struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
struct wcnss_download_nv_req *req;
const struct firmware *fw;
const void *data;
ssize_t left;
int ret;
req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
if (!req)
return;
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
if (ret) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
NVBIN_FILE, ret);
goto free_req;
}
data = fw->data;
left = fw->size;
req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE;
req->last = 0;
req->frag_size = NV_FRAGMENT_SIZE;
req->seq = 0;
do {
if (left <= NV_FRAGMENT_SIZE) {
req->last = 1;
req->frag_size = left;
req->hdr.len = sizeof(*req) + left;
}
memcpy(req->fragment, data, req->frag_size);
ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
if (ret) {
dev_err(wcnss->dev, "failed to send smd packet\n");
goto release_fw;
}
/* Increment for next fragment */
req->seq++;
data += req->hdr.len;
left -= NV_FRAGMENT_SIZE;
} while (left > 0);
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
if (!ret)
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
else if (wcnss->ack_status != 1)
dev_err(wcnss->dev, "nv upload response failed err: %d\n",
wcnss->ack_status);
release_fw:
release_firmware(fw);
free_req:
kfree(req);
}
static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
{
struct wcnss_ctrl *wcnss;
wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL);
if (!wcnss)
return -ENOMEM;
wcnss->dev = &sdev->dev;
wcnss->channel = sdev->channel;
init_completion(&wcnss->ack);
INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);
dev_set_drvdata(&sdev->dev, wcnss);
return wcnss_request_version(wcnss);
}
static const struct qcom_smd_id wcnss_ctrl_smd_match[] = {
{ .name = "WCNSS_CTRL" },
{}
};
static struct qcom_smd_driver wcnss_ctrl_driver = {
.probe = wcnss_ctrl_probe,
.callback = wcnss_ctrl_smd_callback,
.smd_match_table = wcnss_ctrl_smd_match,
.driver = {
.name = "qcom_wcnss_ctrl",
.owner = THIS_MODULE,
},
};
module_qcom_smd_driver(wcnss_ctrl_driver);
MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
MODULE_LICENSE("GPL v2");
......@@ -1044,7 +1044,7 @@ config SERIAL_SGI_IOC3
say Y or M. Otherwise, say N.
config SERIAL_MSM
bool "MSM on-chip serial port support"
tristate "MSM on-chip serial port support"
depends on ARCH_QCOM
select SERIAL_CORE
......
#ifndef __QCOM_SMEM_STATE__
#define __QCOM_SMEM_STATE__
struct qcom_smem_state;
struct qcom_smem_state_ops {
int (*update_bits)(void *, u32, u32);
};
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 *);
int qcom_smem_state_update_bits(struct qcom_smem_state *state, u32 mask, u32 value);
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);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment