cregit-Linux how code gets into the kernel

Release 4.11 drivers/soc/qcom/smd-rpm.c

Directory: drivers/soc/qcom
/*
 * Copyright (c) 2015, Sony Mobile Communications AB.
 * 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/module.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/slab.h>

#include <linux/soc/qcom/smd.h>
#include <linux/soc/qcom/smd-rpm.h>


#define RPM_REQUEST_TIMEOUT     (5 * HZ)

/**
 * struct qcom_smd_rpm - state of the rpm device driver
 * @rpm_channel:        reference to the smd channel
 * @ack:                completion for acks
 * @lock:               mutual exclusion around the send/complete pair
 * @ack_status:         result of the rpm request
 */

struct qcom_smd_rpm {
	
struct qcom_smd_channel *rpm_channel;
	
struct device *dev;

	
struct completion ack;
	
struct mutex lock;
	
int ack_status;
};

/**
 * struct qcom_rpm_header - header for all rpm requests and responses
 * @service_type:       identifier of the service
 * @length:             length of the payload
 */

struct qcom_rpm_header {
	
__le32 service_type;
	
__le32 length;
};

/**
 * struct qcom_rpm_request - request message to the rpm
 * @msg_id:     identifier of the outgoing message
 * @flags:      active/sleep state flags
 * @type:       resource type
 * @id:         resource id
 * @data_len:   length of the payload following this header
 */

struct qcom_rpm_request {
	
__le32 msg_id;
	
__le32 flags;
	
__le32 type;
	
__le32 id;
	
__le32 data_len;
};

/**
 * struct qcom_rpm_message - response message from the rpm
 * @msg_type:   indicator of the type of message
 * @length:     the size of this message, including the message header
 * @msg_id:     message id
 * @message:    textual message from the rpm
 *
 * Multiple of these messages can be stacked in an rpm message.
 */

struct qcom_rpm_message {
	
__le32 msg_type;
	
__le32 length;
	union {
		
__le32 msg_id;
		
u8 message[0];
	};
};


#define RPM_SERVICE_TYPE_REQUEST	0x00716572 
/* "req\0" */


#define RPM_MSG_TYPE_ERR		0x00727265 
/* "err\0" */

#define RPM_MSG_TYPE_MSG_ID		0x2367736d 
/* "msg#" */

/**
 * qcom_rpm_smd_write - write @buf to @type:@id
 * @rpm:        rpm handle
 * @type:       resource type
 * @id:         resource identifier
 * @buf:        the data to be written
 * @count:      number of bytes in @buf
 */

int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, int state, u32 type, u32 id, void *buf, size_t count) { static unsigned msg_id = 1; int left; int ret; struct { struct qcom_rpm_header hdr; struct qcom_rpm_request req; u8 payload[]; } *pkt; size_t size = sizeof(*pkt) + count; /* SMD packets to the RPM may not exceed 256 bytes */ if (WARN_ON(size >= 256)) return -EINVAL; pkt = kmalloc(size, GFP_KERNEL); if (!pkt) return -ENOMEM; mutex_lock(&rpm->lock); pkt->hdr.service_type = cpu_to_le32(RPM_SERVICE_TYPE_REQUEST); pkt->hdr.length = cpu_to_le32(sizeof(struct qcom_rpm_request) + count); pkt->req.msg_id = cpu_to_le32(msg_id++); pkt->req.flags = cpu_to_le32(state); pkt->req.type = cpu_to_le32(type); pkt->req.id = cpu_to_le32(id); pkt->req.data_len = cpu_to_le32(count); memcpy(pkt->payload, buf, count); ret = qcom_smd_send(rpm->rpm_channel, pkt, size); if (ret) goto out; left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); if (!left) ret = -ETIMEDOUT; else ret = rpm->ack_status; out: kfree(pkt); mutex_unlock(&rpm->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson20475.84%250.00%
Stephen Boyd6524.16%250.00%
Total269100.00%4100.00%

EXPORT_SYMBOL(qcom_rpm_smd_write);
static int qcom_smd_rpm_callback(struct qcom_smd_channel *channel, const void *data, size_t count) { const struct qcom_rpm_header *hdr = data; size_t hdr_length = le32_to_cpu(hdr->length); const struct qcom_rpm_message *msg; struct qcom_smd_rpm *rpm = qcom_smd_get_drvdata(channel); const u8 *buf = data + sizeof(struct qcom_rpm_header); const u8 *end = buf + hdr_length; char msgbuf[32]; int status = 0; u32 len, msg_length; if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST || hdr_length < sizeof(struct qcom_rpm_message)) { dev_err(rpm->dev, "invalid request\n"); return 0; } while (buf < end) { msg = (struct qcom_rpm_message *)buf; msg_length = le32_to_cpu(msg->length); switch (le32_to_cpu(msg->msg_type)) { case RPM_MSG_TYPE_MSG_ID: break; case RPM_MSG_TYPE_ERR: len = min_t(u32, ALIGN(msg_length, 4), sizeof(msgbuf)); memcpy_fromio(msgbuf, msg->message, len); msgbuf[len - 1] = 0; if (!strcmp(msgbuf, "resource does not exist")) status = -ENXIO; else status = -EINVAL; break; } buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg_length, 4); } rpm->ack_status = status; complete(&rpm->ack); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson23388.26%266.67%
Stephen Boyd3111.74%133.33%
Total264100.00%3100.00%


static int qcom_smd_rpm_probe(struct qcom_smd_device *sdev) { struct qcom_smd_rpm *rpm; rpm = devm_kzalloc(&sdev->dev, sizeof(*rpm), GFP_KERNEL); if (!rpm) return -ENOMEM; mutex_init(&rpm->lock); init_completion(&rpm->ack); rpm->dev = &sdev->dev; rpm->rpm_channel = sdev->channel; qcom_smd_set_drvdata(sdev->channel, rpm); dev_set_drvdata(&sdev->dev, rpm); return of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson114100.00%2100.00%
Total114100.00%2100.00%


static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev) { of_platform_depopulate(&sdev->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson19100.00%1100.00%
Total19100.00%1100.00%

static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8974" }, {} }; MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); static struct qcom_smd_driver qcom_smd_rpm_driver = { .probe = qcom_smd_rpm_probe, .remove = qcom_smd_rpm_remove, .callback = qcom_smd_rpm_callback, .driver = { .name = "qcom_smd_rpm", .owner = THIS_MODULE, .of_match_table = qcom_smd_rpm_of_match, }, };
static int __init qcom_smd_rpm_init(void) { return qcom_smd_driver_register(&qcom_smd_rpm_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson16100.00%1100.00%
Total16100.00%1100.00%

arch_initcall(qcom_smd_rpm_init);
static void __exit qcom_smd_rpm_exit(void) { qcom_smd_driver_unregister(&qcom_smd_rpm_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson15100.00%1100.00%
Total15100.00%1100.00%

module_exit(qcom_smd_rpm_exit); MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); MODULE_DESCRIPTION("Qualcomm SMD backed RPM driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Björn Andersson81586.89%350.00%
Stephen Boyd10911.62%233.33%
Andy Gross141.49%116.67%
Total938100.00%6100.00%
Directory: drivers/soc/qcom
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.