Qualcomm ARM Based SoC Updates for 4.4

* Implement id_table driver matching in SMD
 * Avoid NULL pointer exception on remove of SMEM
 * Reorder SMEM/SMD configs
 * Make qcom_smem_get() return a pointer
 * Handle big endian CPUs correctly in SMEM
 * Represent SMD channel layout in structures
 * Use __iowrite32_copy() in SMD
 * Remove use of VLAIs in SMD
 * Handle big endian CPUs correctly in SMD/RPM
 * Handle big endian CPUs corretly in SMD
 * Reject sending SMD packets that are too large
 * Fix endianness issue in SCM __qcom_scm_is_call_available
 * Add missing prototype for qcom_scm_is_available()
 * Correct SMEM items for upper channels
 * Use architecture level to build SCM correctly
 * Delete unneeded of_node_put in SMD
 * Correct active/slep state flagging in SMD/RPM
 * Move RPM message ram out of SMEM DT node
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJWHrOuAAoJEFKiBbHx2RXVam0QAIgXhMTuXLNZlJaJ1bFiRqiy
 mAmGpCJl7aN9DPCYXjTzOrPHny0707fZ8lvow7KsjA0OU6JUKP2oD7uSJC9eHyRU
 Mp6B/D2LKjgURozrFsjmfOLT4M+zMtuxk9GyYiBVFT8jqTsyYnlJKbWZVP2PsbMt
 /1HMCuQj7Fe28Qv833P3DMgARRvT0tp32wvpfoVd91GSp2R1BfpDPN6jlaAX5fVz
 8AGMHBoeNSVDDA9J1+3+RZMGKB3Isx5gVodKIkopDQiJSY15Dl32nGKG/6muLdCE
 VMELu1Lsn0e5rFg6Sh8wrtSa/K3ujw929xknikM7sh3+q7bVSowJk8l8Bi/+YfvA
 uL7sjXnhXypXSi45hii+81piScxkRn9o6N7ioj9KIV4Nuciky6YNN8Zr3Ycr2Z2x
 heRz2Ln3A73Cmx4vxEq61lKNIx1qArY+171vsXy0PFQ9Qut4f3UuK6fwnPDL+csC
 ivPPuhGwKicQ2YsvmPMGxj650H7RYALTwstKBGl58y+nhmXnCBt1Ftz7u/btWwHH
 ifk7Q1qPBsxlN1RJzMOSkjJtiaHuiJY132dRROE63P4uWMGUGEWt2iHwwyJL7D0D
 qefh+j4+5/EnYExfgh/lTfNt063qV0wklrHrkvD2q/iG4jAECn1mDNBmA6cLelc5
 3f/27CR6w/XEUMXY6mQ2
 =hprv
 -----END PGP SIGNATURE-----

Merge tag 'qcom-soc-for-4.4' of git://codeaurora.org/quic/kernel/agross-msm into next/drivers

Pull "Qualcomm ARM Based SoC Updates for 4.4" from Andy Gross:

* Implement id_table driver matching in SMD
* Avoid NULL pointer exception on remove of SMEM
* Reorder SMEM/SMD configs
* Make qcom_smem_get() return a pointer
* Handle big endian CPUs correctly in SMEM
* Represent SMD channel layout in structures
* Use __iowrite32_copy() in SMD
* Remove use of VLAIs in SMD
* Handle big endian CPUs correctly in SMD/RPM
* Handle big endian CPUs corretly in SMD
* Reject sending SMD packets that are too large
* Fix endianness issue in SCM __qcom_scm_is_call_available
* Add missing prototype for qcom_scm_is_available()
* Correct SMEM items for upper channels
* Use architecture level to build SCM correctly
* Delete unneeded of_node_put in SMD
* Correct active/slep state flagging in SMD/RPM
* Move RPM message ram out of SMEM DT node

* tag 'qcom-soc-for-4.4' of git://codeaurora.org/quic/kernel/agross-msm:
  soc: qcom: smem: Move RPM message ram out of smem DT node
  soc: qcom: smd-rpm: Correct the active vs sleep state flagging
  soc: qcom: smd: delete unneeded of_node_put
  firmware: qcom-scm: build for correct architecture level
  soc: qcom: smd: Correct SMEM items for upper channels
  qcom-scm: add missing prototype for qcom_scm_is_available()
  qcom-scm: fix endianess issue in __qcom_scm_is_call_available
  soc: qcom: smd: Reject send of too big packets
  soc: qcom: smd: Handle big endian CPUs
  soc: qcom: smd_rpm: Handle big endian CPUs
  soc: qcom: smd: Remove use of VLAIS
  soc: qcom: smd: Use __iowrite32_copy() instead of open-coding it
  soc: qcom: smd: Represent channel layout in structures
  soc: qcom: smem: Handle big endian CPUs
  soc: qcom: Make qcom_smem_get() return a pointer
  soc: qcom: Reorder SMEM/SMD configs
  soc: qcom: smem: Avoid NULL pointer exception on remove
  soc: qcom: smd: Implement id_table driver matching
This commit is contained in:
Arnd Bergmann 2015-10-15 23:03:24 +02:00
commit ead67421a9
10 changed files with 450 additions and 334 deletions

View File

@ -100,6 +100,15 @@
clock-frequency = <19200000>;
};
smem {
compatible = "qcom,smem";
memory-region = <&smem_region>;
qcom,rpm-msg-ram = <&rpm_msg_ram>;
hwlocks = <&tcsr_mutex 3>;
};
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@ -250,13 +259,9 @@
#hwlock-cells = <1>;
};
smem@fa00000 {
compatible = "qcom,smem";
memory-region = <&smem_region>;
rpm_msg_ram: memory@fc428000 {
compatible = "qcom,rpm-msg-ram";
reg = <0xfc428000 0x4000>;
hwlocks = <&tcsr_mutex 3>;
};
blsp1_uart2: serial@f991e000 {

View File

@ -16,7 +16,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o
obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
obj-y += broadcom/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/

View File

@ -480,15 +480,15 @@ void __qcom_scm_cpu_power_down(u32 flags)
int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
{
int ret;
u32 svc_cmd = (svc_id << 10) | cmd_id;
u32 ret_val = 0;
__le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id);
__le32 ret_val = 0;
ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd,
sizeof(svc_cmd), &ret_val, sizeof(ret_val));
if (ret)
return ret;
return ret_val;
return le32_to_cpu(ret_val);
}
int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)

View File

@ -19,6 +19,14 @@ config QCOM_PM
modes. It interface with various system drivers to put the cores in
low power modes.
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
The driver provides an interface to items in a heap shared among all
processors in a Qualcomm platform.
config QCOM_SMD
tristate "Qualcomm Shared Memory Driver (SMD)"
depends on QCOM_SMEM
@ -40,11 +48,3 @@ 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
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
The driver provides an interface to items in a heap shared among all
processors in a Qualcomm platform.

View File

@ -17,6 +17,7 @@
#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>
@ -44,8 +45,8 @@ struct qcom_smd_rpm {
* @length: length of the payload
*/
struct qcom_rpm_header {
u32 service_type;
u32 length;
__le32 service_type;
__le32 length;
};
/**
@ -57,11 +58,11 @@ struct qcom_rpm_header {
* @data_len: length of the payload following this header
*/
struct qcom_rpm_request {
u32 msg_id;
u32 flags;
u32 type;
u32 id;
u32 data_len;
__le32 msg_id;
__le32 flags;
__le32 type;
__le32 id;
__le32 data_len;
};
/**
@ -74,10 +75,10 @@ struct qcom_rpm_request {
* Multiple of these messages can be stacked in an rpm message.
*/
struct qcom_rpm_message {
u32 msg_type;
u32 length;
__le32 msg_type;
__le32 length;
union {
u32 msg_id;
__le32 msg_id;
u8 message[0];
};
};
@ -104,30 +105,34 @@ int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm,
static unsigned msg_id = 1;
int left;
int ret;
struct {
struct qcom_rpm_header hdr;
struct qcom_rpm_request req;
u8 payload[count];
} pkt;
u8 payload[];
} *pkt;
size_t size = sizeof(*pkt) + count;
/* SMD packets to the RPM may not exceed 256 bytes */
if (WARN_ON(sizeof(pkt) >= 256))
if (WARN_ON(size >= 256))
return -EINVAL;
pkt = kmalloc(size, GFP_KERNEL);
if (!pkt)
return -ENOMEM;
mutex_lock(&rpm->lock);
pkt.hdr.service_type = RPM_SERVICE_TYPE_REQUEST;
pkt.hdr.length = sizeof(struct qcom_rpm_request) + count;
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 = msg_id++;
pkt.req.flags = BIT(state);
pkt.req.type = type;
pkt.req.id = id;
pkt.req.data_len = count;
memcpy(pkt.payload, buf, 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, sizeof(pkt));
ret = qcom_smd_send(rpm->rpm_channel, pkt, sizeof(*pkt));
if (ret)
goto out;
@ -138,6 +143,7 @@ int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm,
ret = rpm->ack_status;
out:
kfree(pkt);
mutex_unlock(&rpm->lock);
return ret;
}
@ -148,27 +154,29 @@ static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev,
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 = dev_get_drvdata(&qsdev->dev);
const u8 *buf = data + sizeof(struct qcom_rpm_header);
const u8 *end = buf + hdr->length;
const u8 *end = buf + hdr_length;
char msgbuf[32];
int status = 0;
u32 len;
u32 len, msg_length;
if (hdr->service_type != RPM_SERVICE_TYPE_REQUEST ||
hdr->length < sizeof(struct qcom_rpm_message)) {
if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST ||
hdr_length < sizeof(struct qcom_rpm_message)) {
dev_err(&qsdev->dev, "invalid request\n");
return 0;
}
while (buf < end) {
msg = (struct qcom_rpm_message *)buf;
switch (msg->msg_type) {
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));
len = min_t(u32, ALIGN(msg_length, 4), sizeof(msgbuf));
memcpy_fromio(msgbuf, msg->message, len);
msgbuf[len - 1] = 0;
@ -179,7 +187,7 @@ static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev,
break;
}
buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg->length, 4);
buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg_length, 4);
}
rpm->ack_status = status;

View File

@ -65,7 +65,9 @@
*/
struct smd_channel_info;
struct smd_channel_info_pair;
struct smd_channel_info_word;
struct smd_channel_info_word_pair;
#define SMD_ALLOC_TBL_COUNT 2
#define SMD_ALLOC_TBL_SIZE 64
@ -85,8 +87,8 @@ static const struct {
.fifo_base_id = 338
},
{
.alloc_tbl_id = 14,
.info_base_id = 266,
.alloc_tbl_id = 266,
.info_base_id = 138,
.fifo_base_id = 202,
},
};
@ -151,10 +153,8 @@ enum smd_channel_state {
* @name: name of the channel
* @state: local state of the channel
* @remote_state: remote state of the channel
* @tx_info: byte aligned outgoing channel info
* @rx_info: byte aligned incoming channel info
* @tx_info_word: word aligned outgoing channel info
* @rx_info_word: word aligned incoming channel info
* @info: byte aligned outgoing/incoming channel info
* @info_word: word aligned outgoing/incoming channel info
* @tx_lock: lock to make writes to the channel mutually exclusive
* @fblockread_event: wakeup event tied to tx fBLOCKREADINTR
* @tx_fifo: pointer to the outgoing ring buffer
@ -175,11 +175,8 @@ struct qcom_smd_channel {
enum smd_channel_state state;
enum smd_channel_state remote_state;
struct smd_channel_info *tx_info;
struct smd_channel_info *rx_info;
struct smd_channel_info_word *tx_info_word;
struct smd_channel_info_word *rx_info_word;
struct smd_channel_info_pair *info;
struct smd_channel_info_word_pair *info_word;
struct mutex tx_lock;
wait_queue_head_t fblockread_event;
@ -215,7 +212,7 @@ struct qcom_smd {
* Format of the smd_info smem items, for byte aligned channels.
*/
struct smd_channel_info {
u32 state;
__le32 state;
u8 fDSR;
u8 fCTS;
u8 fCD;
@ -224,46 +221,104 @@ struct smd_channel_info {
u8 fTAIL;
u8 fSTATE;
u8 fBLOCKREADINTR;
u32 tail;
u32 head;
__le32 tail;
__le32 head;
};
struct smd_channel_info_pair {
struct smd_channel_info tx;
struct smd_channel_info rx;
};
/*
* Format of the smd_info smem items, for word aligned channels.
*/
struct smd_channel_info_word {
u32 state;
u32 fDSR;
u32 fCTS;
u32 fCD;
u32 fRI;
u32 fHEAD;
u32 fTAIL;
u32 fSTATE;
u32 fBLOCKREADINTR;
u32 tail;
u32 head;
__le32 state;
__le32 fDSR;
__le32 fCTS;
__le32 fCD;
__le32 fRI;
__le32 fHEAD;
__le32 fTAIL;
__le32 fSTATE;
__le32 fBLOCKREADINTR;
__le32 tail;
__le32 head;
};
#define GET_RX_CHANNEL_INFO(channel, param) \
(channel->rx_info_word ? \
channel->rx_info_word->param : \
channel->rx_info->param)
struct smd_channel_info_word_pair {
struct smd_channel_info_word tx;
struct smd_channel_info_word rx;
};
#define SET_RX_CHANNEL_INFO(channel, param, value) \
(channel->rx_info_word ? \
(channel->rx_info_word->param = value) : \
(channel->rx_info->param = value))
#define GET_RX_CHANNEL_FLAG(channel, param) \
({ \
BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \
channel->info_word ? \
le32_to_cpu(channel->info_word->rx.param) : \
channel->info->rx.param; \
})
#define GET_TX_CHANNEL_INFO(channel, param) \
(channel->tx_info_word ? \
channel->tx_info_word->param : \
channel->tx_info->param)
#define GET_RX_CHANNEL_INFO(channel, param) \
({ \
BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \
le32_to_cpu(channel->info_word ? \
channel->info_word->rx.param : \
channel->info->rx.param); \
})
#define SET_TX_CHANNEL_INFO(channel, param, value) \
(channel->tx_info_word ? \
(channel->tx_info_word->param = value) : \
(channel->tx_info->param = value))
#define SET_RX_CHANNEL_FLAG(channel, param, value) \
({ \
BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \
if (channel->info_word) \
channel->info_word->rx.param = cpu_to_le32(value); \
else \
channel->info->rx.param = value; \
})
#define SET_RX_CHANNEL_INFO(channel, param, value) \
({ \
BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \
if (channel->info_word) \
channel->info_word->rx.param = cpu_to_le32(value); \
else \
channel->info->rx.param = cpu_to_le32(value); \
})
#define GET_TX_CHANNEL_FLAG(channel, param) \
({ \
BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \
channel->info_word ? \
le32_to_cpu(channel->info_word->tx.param) : \
channel->info->tx.param; \
})
#define GET_TX_CHANNEL_INFO(channel, param) \
({ \
BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \
le32_to_cpu(channel->info_word ? \
channel->info_word->tx.param : \
channel->info->tx.param); \
})
#define SET_TX_CHANNEL_FLAG(channel, param, value) \
({ \
BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \
if (channel->info_word) \
channel->info_word->tx.param = cpu_to_le32(value); \
else \
channel->info->tx.param = value; \
})
#define SET_TX_CHANNEL_INFO(channel, param, value) \
({ \
BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \
if (channel->info_word) \
channel->info_word->tx.param = cpu_to_le32(value); \
else \
channel->info->tx.param = cpu_to_le32(value); \
})
/**
* struct qcom_smd_alloc_entry - channel allocation entry
@ -274,9 +329,9 @@ struct smd_channel_info_word {
*/
struct qcom_smd_alloc_entry {
u8 name[20];
u32 cid;
u32 flags;
u32 ref_count;
__le32 cid;
__le32 flags;
__le32 ref_count;
} __packed;
#define SMD_CHANNEL_FLAGS_EDGE_MASK 0xff
@ -305,14 +360,14 @@ static void qcom_smd_signal_channel(struct qcom_smd_channel *channel)
static void qcom_smd_channel_reset(struct qcom_smd_channel *channel)
{
SET_TX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED);
SET_TX_CHANNEL_INFO(channel, fDSR, 0);
SET_TX_CHANNEL_INFO(channel, fCTS, 0);
SET_TX_CHANNEL_INFO(channel, fCD, 0);
SET_TX_CHANNEL_INFO(channel, fRI, 0);
SET_TX_CHANNEL_INFO(channel, fHEAD, 0);
SET_TX_CHANNEL_INFO(channel, fTAIL, 0);
SET_TX_CHANNEL_INFO(channel, fSTATE, 1);
SET_TX_CHANNEL_INFO(channel, fBLOCKREADINTR, 1);
SET_TX_CHANNEL_FLAG(channel, fDSR, 0);
SET_TX_CHANNEL_FLAG(channel, fCTS, 0);
SET_TX_CHANNEL_FLAG(channel, fCD, 0);
SET_TX_CHANNEL_FLAG(channel, fRI, 0);
SET_TX_CHANNEL_FLAG(channel, fHEAD, 0);
SET_TX_CHANNEL_FLAG(channel, fTAIL, 0);
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);
@ -350,12 +405,12 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state);
SET_TX_CHANNEL_INFO(channel, fDSR, is_open);
SET_TX_CHANNEL_INFO(channel, fCTS, is_open);
SET_TX_CHANNEL_INFO(channel, fCD, is_open);
SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
SET_TX_CHANNEL_FLAG(channel, fCD, is_open);
SET_TX_CHANNEL_INFO(channel, state, state);
SET_TX_CHANNEL_INFO(channel, fSTATE, 1);
SET_TX_CHANNEL_FLAG(channel, fSTATE, 1);
channel->state = state;
qcom_smd_signal_channel(channel);
@ -364,20 +419,15 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
/*
* Copy count bytes of data using 32bit accesses, if that's required.
*/
static void smd_copy_to_fifo(void __iomem *_dst,
const void *_src,
static void smd_copy_to_fifo(void __iomem *dst,
const void *src,
size_t count,
bool word_aligned)
{
u32 *dst = (u32 *)_dst;
u32 *src = (u32 *)_src;
if (word_aligned) {
count /= sizeof(u32);
while (count--)
writel_relaxed(*src++, dst++);
__iowrite32_copy(dst, src, count / sizeof(u32));
} else {
memcpy_toio(_dst, _src, count);
memcpy_toio(dst, src, count);
}
}
@ -395,7 +445,7 @@ static void smd_copy_from_fifo(void *_dst,
if (word_aligned) {
count /= sizeof(u32);
while (count--)
*dst++ = readl_relaxed(src++);
*dst++ = __raw_readl(src++);
} else {
memcpy_fromio(_dst, _src, count);
}
@ -412,7 +462,7 @@ static size_t qcom_smd_channel_peek(struct qcom_smd_channel *channel,
unsigned tail;
size_t len;
word_aligned = channel->rx_info_word != NULL;
word_aligned = channel->info_word;
tail = GET_RX_CHANNEL_INFO(channel, tail);
len = min_t(size_t, count, channel->fifo_size - tail);
@ -491,7 +541,7 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel)
{
bool need_state_scan = false;
int remote_state;
u32 pktlen;
__le32 pktlen;
int avail;
int ret;
@ -502,10 +552,10 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel)
need_state_scan = true;
}
/* Indicate that we have seen any state change */
SET_RX_CHANNEL_INFO(channel, fSTATE, 0);
SET_RX_CHANNEL_FLAG(channel, fSTATE, 0);
/* Signal waiting qcom_smd_send() about the interrupt */
if (!GET_TX_CHANNEL_INFO(channel, fBLOCKREADINTR))
if (!GET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR))
wake_up_interruptible(&channel->fblockread_event);
/* Don't consume any data until we've opened the channel */
@ -513,7 +563,7 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel)
goto out;
/* Indicate that we've seen the new data */
SET_RX_CHANNEL_INFO(channel, fHEAD, 0);
SET_RX_CHANNEL_FLAG(channel, fHEAD, 0);
/* Consume data */
for (;;) {
@ -522,7 +572,7 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel)
if (!channel->pkt_size && avail >= SMD_PACKET_HEADER_LEN) {
qcom_smd_channel_peek(channel, &pktlen, sizeof(pktlen));
qcom_smd_channel_advance(channel, SMD_PACKET_HEADER_LEN);
channel->pkt_size = pktlen;
channel->pkt_size = le32_to_cpu(pktlen);
} else if (channel->pkt_size && avail >= channel->pkt_size) {
ret = qcom_smd_channel_recv_single(channel);
if (ret)
@ -533,10 +583,10 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel)
}
/* Indicate that we have seen and updated tail */
SET_RX_CHANNEL_INFO(channel, fTAIL, 1);
SET_RX_CHANNEL_FLAG(channel, fTAIL, 1);
/* Signal the remote that we've consumed the data (if requested) */
if (!GET_RX_CHANNEL_INFO(channel, fBLOCKREADINTR)) {
if (!GET_RX_CHANNEL_FLAG(channel, fBLOCKREADINTR)) {
/* Ensure ordering of channel info updates */
wmb();
@ -627,7 +677,7 @@ static int qcom_smd_write_fifo(struct qcom_smd_channel *channel,
unsigned head;
size_t len;
word_aligned = channel->tx_info_word != NULL;
word_aligned = channel->info_word;
head = GET_TX_CHANNEL_INFO(channel, head);
len = min_t(size_t, count, channel->fifo_size - head);
@ -665,12 +715,16 @@ static int qcom_smd_write_fifo(struct qcom_smd_channel *channel,
*/
int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len)
{
u32 hdr[5] = {len,};
__le32 hdr[5] = { cpu_to_le32(len), };
int tlen = sizeof(hdr) + len;
int ret;
/* Word aligned channels only accept word size aligned data */
if (channel->rx_info_word != NULL && len % 4)
if (channel->info_word && len % 4)
return -EINVAL;
/* Reject packets that are too big */
if (tlen >= channel->fifo_size)
return -EINVAL;
ret = mutex_lock_interruptible(&channel->tx_lock);
@ -683,7 +737,7 @@ int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len)
goto out;
}
SET_TX_CHANNEL_INFO(channel, fBLOCKREADINTR, 0);
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 0);
ret = wait_event_interruptible(channel->fblockread_event,
qcom_smd_get_tx_avail(channel) >= tlen ||
@ -691,15 +745,15 @@ int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len)
if (ret)
goto out;
SET_TX_CHANNEL_INFO(channel, fBLOCKREADINTR, 1);
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
}
SET_TX_CHANNEL_INFO(channel, fTAIL, 0);
SET_TX_CHANNEL_FLAG(channel, fTAIL, 0);
qcom_smd_write_fifo(channel, hdr, sizeof(hdr));
qcom_smd_write_fifo(channel, data, len);
SET_TX_CHANNEL_INFO(channel, fHEAD, 1);
SET_TX_CHANNEL_FLAG(channel, fHEAD, 1);
/* Ensure ordering of channel info updates */
wmb();
@ -727,6 +781,19 @@ static struct qcom_smd_driver *to_smd_driver(struct device *dev)
static int qcom_smd_dev_match(struct device *dev, struct device_driver *drv)
{
struct qcom_smd_device *qsdev = to_smd_device(dev);
struct qcom_smd_driver *qsdrv = container_of(drv, struct qcom_smd_driver, driver);
const struct qcom_smd_id *match = qsdrv->smd_match_table;
const char *name = qsdev->channel->name;
if (match) {
while (match->name[0]) {
if (!strcmp(match->name, name))
return 1;
match++;
}
}
return of_driver_match_device(dev, drv);
}
@ -854,10 +921,8 @@ static struct device_node *qcom_smd_match_channel(struct device_node *edge_node,
for_each_available_child_of_node(edge_node, child) {
key = "qcom,smd-channels";
ret = of_property_read_string(child, key, &name);
if (ret) {
of_node_put(child);
if (ret)
continue;
}
if (strcmp(name, channel) == 0)
return child;
@ -880,19 +945,17 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
if (channel->qsdev)
return -EEXIST;
node = qcom_smd_match_channel(edge->of_node, channel->name);
if (!node) {
dev_dbg(smd->dev, "no match for '%s'\n", channel->name);
return -ENXIO;
}
dev_dbg(smd->dev, "registering '%s'\n", channel->name);
qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
if (!qsdev)
return -ENOMEM;
dev_set_name(&qsdev->dev, "%s.%s", edge->of_node->name, node->name);
node = qcom_smd_match_channel(edge->of_node, channel->name);
dev_set_name(&qsdev->dev, "%s.%s",
edge->of_node->name,
node ? node->name : channel->name);
qsdev->dev.parent = smd->dev;
qsdev->dev.bus = &qcom_smd_bus;
qsdev->dev.release = qcom_smd_release_device;
@ -978,21 +1041,20 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
spin_lock_init(&channel->recv_lock);
init_waitqueue_head(&channel->fblockread_event);
ret = qcom_smem_get(edge->remote_pid, smem_info_item, (void **)&info,
&info_size);
if (ret)
info = qcom_smem_get(edge->remote_pid, smem_info_item, &info_size);
if (IS_ERR(info)) {
ret = PTR_ERR(info);
goto free_name_and_channel;
}
/*
* Use the size of the item to figure out which channel info struct to
* use.
*/
if (info_size == 2 * sizeof(struct smd_channel_info_word)) {
channel->tx_info_word = info;
channel->rx_info_word = info + sizeof(struct smd_channel_info_word);
channel->info_word = info;
} else if (info_size == 2 * sizeof(struct smd_channel_info)) {
channel->tx_info = info;
channel->rx_info = info + sizeof(struct smd_channel_info);
channel->info = info;
} else {
dev_err(smd->dev,
"channel info of size %zu not supported\n", info_size);
@ -1000,10 +1062,11 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
goto free_name_and_channel;
}
ret = qcom_smem_get(edge->remote_pid, smem_fifo_item, &fifo_base,
&fifo_size);
if (ret)
fifo_base = qcom_smem_get(edge->remote_pid, smem_fifo_item, &fifo_size);
if (IS_ERR(fifo_base)) {
ret = PTR_ERR(fifo_base);
goto free_name_and_channel;
}
/* The channel consist of a rx and tx fifo of equal size */
fifo_size /= 2;
@ -1040,20 +1103,19 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)
unsigned long flags;
unsigned fifo_id;
unsigned info_id;
int ret;
int tbl;
int i;
u32 eflags, cid;
for (tbl = 0; tbl < SMD_ALLOC_TBL_COUNT; tbl++) {
ret = qcom_smem_get(edge->remote_pid,
smem_items[tbl].alloc_tbl_id,
(void **)&alloc_tbl,
NULL);
if (ret < 0)
alloc_tbl = qcom_smem_get(edge->remote_pid,
smem_items[tbl].alloc_tbl_id, NULL);
if (IS_ERR(alloc_tbl))
continue;
for (i = 0; i < SMD_ALLOC_TBL_SIZE; i++) {
entry = &alloc_tbl[i];
eflags = le32_to_cpu(entry->flags);
if (test_bit(i, edge->allocated[tbl]))
continue;
@ -1063,14 +1125,15 @@ static void qcom_discover_channels(struct qcom_smd_edge *edge)
if (!entry->name[0])
continue;
if (!(entry->flags & SMD_CHANNEL_FLAGS_PACKET))
if (!(eflags & SMD_CHANNEL_FLAGS_PACKET))
continue;
if ((entry->flags & SMD_CHANNEL_FLAGS_EDGE_MASK) != edge->edge_id)
if ((eflags & SMD_CHANNEL_FLAGS_EDGE_MASK) != edge->edge_id)
continue;
info_id = smem_items[tbl].info_base_id + entry->cid;
fifo_id = smem_items[tbl].fifo_base_id + entry->cid;
cid = le32_to_cpu(entry->cid);
info_id = smem_items[tbl].info_base_id + cid;
fifo_id = smem_items[tbl].fifo_base_id + cid;
channel = qcom_smd_create_channel(edge, info_id, fifo_id, entry->name);
if (IS_ERR(channel))
@ -1227,11 +1290,12 @@ static int qcom_smd_probe(struct platform_device *pdev)
int num_edges;
int ret;
int i = 0;
void *p;
/* Wait for smem */
ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, smem_items[0].alloc_tbl_id, NULL, NULL);
if (ret == -EPROBE_DEFER)
return ret;
p = qcom_smem_get(QCOM_SMEM_HOST_ANY, smem_items[0].alloc_tbl_id, NULL);
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);

View File

@ -92,9 +92,9 @@
* @params: parameters to the command
*/
struct smem_proc_comm {
u32 command;
u32 status;
u32 params[2];
__le32 command;
__le32 status;
__le32 params[2];
};
/**
@ -106,10 +106,10 @@ struct smem_proc_comm {
* the default region. bits 0,1 are reserved
*/
struct smem_global_entry {
u32 allocated;
u32 offset;
u32 size;
u32 aux_base; /* bits 1:0 reserved */
__le32 allocated;
__le32 offset;
__le32 size;
__le32 aux_base; /* bits 1:0 reserved */
};
#define AUX_BASE_MASK 0xfffffffc
@ -125,11 +125,11 @@ struct smem_global_entry {
*/
struct smem_header {
struct smem_proc_comm proc_comm[4];
u32 version[32];
u32 initialized;
u32 free_offset;
u32 available;
u32 reserved;
__le32 version[32];
__le32 initialized;
__le32 free_offset;
__le32 available;
__le32 reserved;
struct smem_global_entry toc[SMEM_ITEM_COUNT];
};
@ -143,12 +143,12 @@ struct smem_header {
* @reserved: reserved entries for later use
*/
struct smem_ptable_entry {
u32 offset;
u32 size;
u32 flags;
u16 host0;
u16 host1;
u32 reserved[8];
__le32 offset;
__le32 size;
__le32 flags;
__le16 host0;
__le16 host1;
__le32 reserved[8];
};
/**
@ -160,13 +160,14 @@ struct smem_ptable_entry {
* @entry: list of @smem_ptable_entry for the @num_entries partitions
*/
struct smem_ptable {
u32 magic;
u32 version;
u32 num_entries;
u32 reserved[5];
u8 magic[4];
__le32 version;
__le32 num_entries;
__le32 reserved[5];
struct smem_ptable_entry entry[];
};
#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$TOC" */
static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
/**
* struct smem_partition_header - header of the partitions
@ -181,15 +182,16 @@ struct smem_ptable {
* @reserved: for now reserved entries
*/
struct smem_partition_header {
u32 magic;
u16 host0;
u16 host1;
u32 size;
u32 offset_free_uncached;
u32 offset_free_cached;
u32 reserved[3];
u8 magic[4];
__le16 host0;
__le16 host1;
__le32 size;
__le32 offset_free_uncached;
__le32 offset_free_cached;
__le32 reserved[3];
};
#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */
static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
/**
* struct smem_private_entry - header of each item in the private partition
@ -201,12 +203,12 @@ struct smem_partition_header {
* @reserved: for now reserved entry
*/
struct smem_private_entry {
u16 canary;
u16 item;
u32 size; /* includes padding bytes */
u16 padding_data;
u16 padding_hdr;
u32 reserved;
u16 canary; /* bytes are the same so no swapping needed */
__le16 item;
__le32 size; /* includes padding bytes */
__le16 padding_data;
__le16 padding_hdr;
__le32 reserved;
};
#define SMEM_PRIVATE_CANARY 0xa5a5
@ -242,6 +244,45 @@ struct qcom_smem {
struct smem_region regions[0];
};
static struct smem_private_entry *
phdr_to_last_private_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
return p + le32_to_cpu(phdr->offset_free_uncached);
}
static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
return p + le32_to_cpu(phdr->offset_free_cached);
}
static struct smem_private_entry *
phdr_to_first_private_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
return p + sizeof(*phdr);
}
static struct smem_private_entry *
private_entry_next(struct smem_private_entry *e)
{
void *p = e;
return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
le32_to_cpu(e->size);
}
static void *entry_to_item(struct smem_private_entry *e)
{
void *p = e;
return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
}
/* Pointer to the one and only smem handle */
static struct qcom_smem *__smem;
@ -254,16 +295,16 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
size_t size)
{
struct smem_partition_header *phdr;
struct smem_private_entry *hdr;
struct smem_private_entry *hdr, *end;
size_t alloc_size;
void *p;
void *cached;
phdr = smem->partitions[host];
hdr = phdr_to_first_private_entry(phdr);
end = phdr_to_last_private_entry(phdr);
cached = phdr_to_first_cached_entry(phdr);
p = (void *)phdr + sizeof(*phdr);
while (p < (void *)phdr + phdr->offset_free_uncached) {
hdr = p;
while (hdr < end) {
if (hdr->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d partition\n",
@ -271,24 +312,23 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
return -EINVAL;
}
if (hdr->item == item)
if (le16_to_cpu(hdr->item) == item)
return -EEXIST;
p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
hdr = private_entry_next(hdr);
}
/* Check that we don't grow into the cached region */
alloc_size = sizeof(*hdr) + ALIGN(size, 8);
if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) {
if ((void *)hdr + alloc_size >= cached) {
dev_err(smem->dev, "Out of memory\n");
return -ENOSPC;
}
hdr = p;
hdr->canary = SMEM_PRIVATE_CANARY;
hdr->item = item;
hdr->size = ALIGN(size, 8);
hdr->padding_data = hdr->size - size;
hdr->item = cpu_to_le16(item);
hdr->size = cpu_to_le32(ALIGN(size, 8));
hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
hdr->padding_hdr = 0;
/*
@ -297,7 +337,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
* gets a consistent view of the linked list.
*/
wmb();
phdr->offset_free_uncached += alloc_size;
le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
return 0;
}
@ -318,11 +358,11 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
return -EEXIST;
size = ALIGN(size, 8);
if (WARN_ON(size > header->available))
if (WARN_ON(size > le32_to_cpu(header->available)))
return -ENOMEM;
entry->offset = header->free_offset;
entry->size = size;
entry->size = cpu_to_le32(size);
/*
* Ensure the header is consistent before we mark the item allocated,
@ -330,10 +370,10 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
* even though they do not take the spinlock on read.
*/
wmb();
entry->allocated = 1;
entry->allocated = cpu_to_le32(1);
header->free_offset += size;
header->available -= size;
le32_add_cpu(&header->free_offset, size);
le32_add_cpu(&header->available, -size);
return 0;
}
@ -378,10 +418,9 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
}
EXPORT_SYMBOL(qcom_smem_alloc);
static int qcom_smem_get_global(struct qcom_smem *smem,
unsigned item,
void **ptr,
size_t *size)
static void *qcom_smem_get_global(struct qcom_smem *smem,
unsigned item,
size_t *size)
{
struct smem_header *header;
struct smem_region *area;
@ -390,100 +429,94 @@ static int qcom_smem_get_global(struct qcom_smem *smem,
unsigned i;
if (WARN_ON(item >= SMEM_ITEM_COUNT))
return -EINVAL;
return ERR_PTR(-EINVAL);
header = smem->regions[0].virt_base;
entry = &header->toc[item];
if (!entry->allocated)
return -ENXIO;
return ERR_PTR(-ENXIO);
if (ptr != NULL) {
aux_base = entry->aux_base & AUX_BASE_MASK;
aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
for (i = 0; i < smem->num_regions; i++) {
area = &smem->regions[i];
for (i = 0; i < smem->num_regions; i++) {
area = &smem->regions[i];
if (area->aux_base == aux_base || !aux_base) {
*ptr = area->virt_base + entry->offset;
break;
}
if (area->aux_base == aux_base || !aux_base) {
if (size != NULL)
*size = le32_to_cpu(entry->size);
return area->virt_base + le32_to_cpu(entry->offset);
}
}
if (size != NULL)
*size = entry->size;
return 0;
return ERR_PTR(-ENOENT);
}
static int qcom_smem_get_private(struct qcom_smem *smem,
unsigned host,
unsigned item,
void **ptr,
size_t *size)
static void *qcom_smem_get_private(struct qcom_smem *smem,
unsigned host,
unsigned item,
size_t *size)
{
struct smem_partition_header *phdr;
struct smem_private_entry *hdr;
void *p;
struct smem_private_entry *e, *end;
phdr = smem->partitions[host];
e = phdr_to_first_private_entry(phdr);
end = phdr_to_last_private_entry(phdr);
p = (void *)phdr + sizeof(*phdr);
while (p < (void *)phdr + phdr->offset_free_uncached) {
hdr = p;
if (hdr->canary != SMEM_PRIVATE_CANARY) {
while (e < end) {
if (e->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d partition\n",
host);
return -EINVAL;
return ERR_PTR(-EINVAL);
}
if (hdr->item == item) {
if (ptr != NULL)
*ptr = p + sizeof(*hdr) + hdr->padding_hdr;
if (le16_to_cpu(e->item) == item) {
if (size != NULL)
*size = hdr->size - hdr->padding_data;
*size = le32_to_cpu(e->size) -
le16_to_cpu(e->padding_data);
return 0;
return entry_to_item(e);
}
p += sizeof(*hdr) + hdr->padding_hdr + hdr->size;
e = private_entry_next(e);
}
return -ENOENT;
return ERR_PTR(-ENOENT);
}
/**
* qcom_smem_get() - resolve ptr of size of a smem item
* @host: the remote processor, or -1
* @item: smem item handle
* @ptr: pointer to be filled out with address of the item
* @size: pointer to be filled out with size of the item
*
* Looks up pointer and size of a smem item.
* Looks up smem item and returns pointer to it. Size of smem
* item is returned in @size.
*/
int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size)
void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
{
unsigned long flags;
int ret;
void *ptr = ERR_PTR(-EPROBE_DEFER);
if (!__smem)
return -EPROBE_DEFER;
return ptr;
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
HWSPINLOCK_TIMEOUT,
&flags);
if (ret)
return ret;
return ERR_PTR(ret);
if (host < SMEM_HOST_COUNT && __smem->partitions[host])
ret = qcom_smem_get_private(__smem, host, item, ptr, size);
ptr = qcom_smem_get_private(__smem, host, item, size);
else
ret = qcom_smem_get_global(__smem, item, ptr, size);
ptr = qcom_smem_get_global(__smem, item, size);
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
return ret;
return ptr;
}
EXPORT_SYMBOL(qcom_smem_get);
@ -506,10 +539,11 @@ int qcom_smem_get_free_space(unsigned host)
if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
phdr = __smem->partitions[host];
ret = phdr->offset_free_cached - phdr->offset_free_uncached;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
} else {
header = __smem->regions[0].virt_base;
ret = header->available;
ret = le32_to_cpu(header->available);
}
return ret;
@ -518,13 +552,11 @@ EXPORT_SYMBOL(qcom_smem_get_free_space);
static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
{
unsigned *versions;
__le32 *versions;
size_t size;
int ret;
ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION,
(void **)&versions, &size);
if (ret < 0) {
versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size);
if (IS_ERR(versions)) {
dev_err(smem->dev, "Unable to read the version item\n");
return -ENOENT;
}
@ -534,7 +566,7 @@ static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
return -EINVAL;
}
return versions[SMEM_MASTER_SBL_VERSION_INDEX];
return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
}
static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
@ -544,35 +576,38 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
unsigned remote_host;
u32 version, host0, host1;
int i;
ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
if (ptable->magic != SMEM_PTABLE_MAGIC)
if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
return 0;
if (ptable->version != 1) {
version = le32_to_cpu(ptable->version);
if (version != 1) {
dev_err(smem->dev,
"Unsupported partition header version %d\n",
ptable->version);
"Unsupported partition header version %d\n", version);
return -EINVAL;
}
for (i = 0; i < ptable->num_entries; i++) {
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
host0 = le16_to_cpu(entry->host0);
host1 = le16_to_cpu(entry->host1);
if (entry->host0 != local_host && entry->host1 != local_host)
if (host0 != local_host && host1 != local_host)
continue;
if (!entry->offset)
if (!le32_to_cpu(entry->offset))
continue;
if (!entry->size)
if (!le32_to_cpu(entry->size))
continue;
if (entry->host0 == local_host)
remote_host = entry->host1;
if (host0 == local_host)
remote_host = host1;
else
remote_host = entry->host0;
remote_host = host0;
if (remote_host >= SMEM_HOST_COUNT) {
dev_err(smem->dev,
@ -588,21 +623,24 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
return -EINVAL;
}
header = smem->regions[0].virt_base + entry->offset;
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
host0 = le16_to_cpu(header->host0);
host1 = le16_to_cpu(header->host1);
if (header->magic != SMEM_PART_MAGIC) {
if (memcmp(header->magic, SMEM_PART_MAGIC,
sizeof(header->magic))) {
dev_err(smem->dev,
"Partition %d has invalid magic\n", i);
return -EINVAL;
}
if (header->host0 != local_host && header->host1 != local_host) {
if (host0 != local_host && host1 != local_host) {
dev_err(smem->dev,
"Partition %d hosts are invalid\n", i);
return -EINVAL;
}
if (header->host0 != remote_host && header->host1 != remote_host) {
if (host0 != remote_host && host1 != remote_host) {
dev_err(smem->dev,
"Partition %d hosts are invalid\n", i);
return -EINVAL;
@ -614,7 +652,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
return -EINVAL;
}
if (header->offset_free_uncached > header->size) {
if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
dev_err(smem->dev,
"Partition %d has invalid free pointer\n", i);
return -EINVAL;
@ -626,37 +664,47 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
return 0;
}
static int qcom_smem_count_mem_regions(struct platform_device *pdev)
static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
const char *name, int i)
{
struct resource *res;
int num_regions = 0;
int i;
struct device_node *np;
struct resource r;
int ret;
for (i = 0; i < pdev->num_resources; i++) {
res = &pdev->resource[i];
if (resource_type(res) == IORESOURCE_MEM)
num_regions++;
np = of_parse_phandle(dev->of_node, name, 0);
if (!np) {
dev_err(dev, "No %s specified\n", name);
return -EINVAL;
}
return num_regions;
ret = of_address_to_resource(np, 0, &r);
of_node_put(np);
if (ret)
return ret;
smem->regions[i].aux_base = (u32)r.start;
smem->regions[i].size = resource_size(&r);
smem->regions[i].virt_base = devm_ioremap_nocache(dev, r.start,
resource_size(&r));
if (!smem->regions[i].virt_base)
return -ENOMEM;
return 0;
}
static int qcom_smem_probe(struct platform_device *pdev)
{
struct smem_header *header;
struct device_node *np;
struct qcom_smem *smem;
struct resource *res;
struct resource r;
size_t array_size;
int num_regions = 0;
int num_regions;
int hwlock_id;
u32 version;
int ret;
int i;
num_regions = qcom_smem_count_mem_regions(pdev) + 1;
num_regions = 1;
if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL))
num_regions++;
array_size = num_regions * sizeof(struct smem_region);
smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL);
@ -666,39 +714,17 @@ static int qcom_smem_probe(struct platform_device *pdev)
smem->dev = &pdev->dev;
smem->num_regions = num_regions;
np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
if (!np) {
dev_err(&pdev->dev, "No memory-region specified\n");
return -EINVAL;
}
ret = of_address_to_resource(np, 0, &r);
of_node_put(np);
ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0);
if (ret)
return ret;
smem->regions[0].aux_base = (u32)r.start;
smem->regions[0].size = resource_size(&r);
smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev,
r.start,
resource_size(&r));
if (!smem->regions[0].virt_base)
return -ENOMEM;
for (i = 1; i < num_regions; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1);
smem->regions[i].aux_base = (u32)res->start;
smem->regions[i].size = resource_size(res);
smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev,
res->start,
resource_size(res));
if (!smem->regions[i].virt_base)
return -ENOMEM;
}
if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev,
"qcom,rpm-msg-ram", 1)))
return ret;
header = smem->regions[0].virt_base;
if (header->initialized != 1 || header->reserved) {
if (le32_to_cpu(header->initialized) != 1 ||
le32_to_cpu(header->reserved)) {
dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
return -EINVAL;
}
@ -730,8 +756,8 @@ static int qcom_smem_probe(struct platform_device *pdev)
static int qcom_smem_remove(struct platform_device *pdev)
{
__smem = NULL;
hwspin_lock_free(__smem->hwlock);
__smem = NULL;
return 0;
}

View File

@ -23,6 +23,8 @@ struct qcom_scm_hdcp_req {
u32 val;
};
extern bool qcom_scm_is_available(void);
extern bool qcom_scm_hdcp_available(void);
extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
u32 *resp);

View File

@ -8,6 +8,14 @@ struct qcom_smd;
struct qcom_smd_channel;
struct qcom_smd_lookup;
/**
* struct qcom_smd_id - struct used for matching a smd device
* @name: name of the channel
*/
struct qcom_smd_id {
char name[20];
};
/**
* struct qcom_smd_device - smd device struct
* @dev: the device struct
@ -21,6 +29,7 @@ struct qcom_smd_device {
/**
* struct qcom_smd_driver - smd driver struct
* @driver: underlying device driver
* @smd_match_table: static channel match table
* @probe: invoked when the smd channel is found
* @remove: invoked when the smd channel is closed
* @callback: invoked when an inbound message is received on the channel,
@ -29,6 +38,8 @@ struct qcom_smd_device {
*/
struct qcom_smd_driver {
struct device_driver driver;
const struct qcom_smd_id *smd_match_table;
int (*probe)(struct qcom_smd_device *dev);
void (*remove)(struct qcom_smd_device *dev);
int (*callback)(struct qcom_smd_device *, const void *, size_t);

View File

@ -4,7 +4,7 @@
#define QCOM_SMEM_HOST_ANY -1
int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size);
void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
int qcom_smem_get_free_space(unsigned host);