mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
26
drivers/misc/mcu_ipc/Kconfig
Normal file
26
drivers/misc/mcu_ipc/Kconfig
Normal file
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# MCU_IPC Device Driver
|
||||
#
|
||||
|
||||
config MCU_IPC
|
||||
bool "MCU IPC Support"
|
||||
default n
|
||||
help
|
||||
This enables MCU_IPC driver to control the MCU_IPC Device.
|
||||
|
||||
MCU_IPC is the Mailbox which has 16 interrupts for TX/RX each
|
||||
and 256 bytes memory for communicating messages.
|
||||
AP and CP can share the messages through this device.
|
||||
|
||||
config MCU_IPC_TEST
|
||||
bool "MCU_IPC driver test"
|
||||
depends on MCU_IPC
|
||||
help
|
||||
This enables MCU_IPC_TEST for checking mailbox at probe time.
|
||||
|
||||
config SHM_IPC
|
||||
bool "Shared Memory for IPC support"
|
||||
default n
|
||||
help
|
||||
This enables SHM_IPC driver to control the Shared memory
|
||||
for AP-CP Interface.
|
6
drivers/misc/mcu_ipc/Makefile
Normal file
6
drivers/misc/mcu_ipc/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# MCU_IPC Device Driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MCU_IPC) += mcu_ipc.o
|
||||
obj-$(CONFIG_SHM_IPC) += shm_ipc.o
|
353
drivers/misc/mcu_ipc/mcu_ipc.c
Normal file
353
drivers/misc/mcu_ipc/mcu_ipc.c
Normal file
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Samsung Electronics Co.Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* MCU IPC driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "regs-mcu_ipc.h"
|
||||
#include "mcu_ipc.h"
|
||||
|
||||
static irqreturn_t mcu_ipc_handler(int irq, void *data)
|
||||
{
|
||||
u32 irq_stat, i;
|
||||
u32 id;
|
||||
|
||||
id = ((struct mcu_ipc_drv_data *)data)->id;
|
||||
irq_stat = mcu_ipc_readl(id, EXYNOS_MCU_IPC_INTSR0) & 0xFFFF0000;
|
||||
/* Interrupt Clear */
|
||||
mcu_ipc_writel(id, irq_stat, EXYNOS_MCU_IPC_INTCR0);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (irq_stat & (1 << (i + 16))) {
|
||||
if ((1 << (i + 16)) & mcu_dat[id].registered_irq)
|
||||
mcu_dat[id].hd[i].handler(mcu_dat[id].hd[i].data);
|
||||
else
|
||||
dev_err(mcu_dat[id].mcu_ipc_dev,
|
||||
"Unregistered INT received.\n");
|
||||
|
||||
irq_stat &= ~(1 << (i + 16));
|
||||
}
|
||||
|
||||
if (!irq_stat)
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mbox_request_irq(enum mcu_ipc_region id, u32 int_num,
|
||||
void (*handler)(void *), void *data)
|
||||
{
|
||||
if ((!handler) || (int_num > 15))
|
||||
return -EINVAL;
|
||||
|
||||
mcu_dat[id].hd[int_num].data = data;
|
||||
mcu_dat[id].hd[int_num].handler = handler;
|
||||
mcu_dat[id].registered_irq |= 1 << (int_num + 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_request_irq);
|
||||
|
||||
int mcu_ipc_unregister_handler(enum mcu_ipc_region id, u32 int_num,
|
||||
void (*handler)(void *))
|
||||
{
|
||||
if (!handler || (mcu_dat[id].hd[int_num].handler != handler))
|
||||
return -EINVAL;
|
||||
|
||||
mcu_dat[id].hd[int_num].data = NULL;
|
||||
mcu_dat[id].hd[int_num].handler = NULL;
|
||||
mcu_dat[id].registered_irq &= ~(1 << (int_num + 16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mcu_ipc_unregister_handler);
|
||||
|
||||
void mbox_set_interrupt(enum mcu_ipc_region id, u32 int_num)
|
||||
{
|
||||
/* generate interrupt */
|
||||
if (int_num < 16)
|
||||
mcu_ipc_writel(id, 0x1 << int_num, EXYNOS_MCU_IPC_INTGR1);
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_set_interrupt);
|
||||
|
||||
void mcu_ipc_send_command(enum mcu_ipc_region id, u32 int_num, u16 cmd)
|
||||
{
|
||||
/* write command */
|
||||
if (int_num < 16)
|
||||
mcu_ipc_writel(id, cmd, EXYNOS_MCU_IPC_ISSR0 + (8 * int_num));
|
||||
|
||||
/* generate interrupt */
|
||||
mbox_set_interrupt(id, int_num);
|
||||
}
|
||||
EXPORT_SYMBOL(mcu_ipc_send_command);
|
||||
|
||||
u32 mbox_get_value(enum mcu_ipc_region id, u32 mbx_num)
|
||||
{
|
||||
if (mbx_num < 64)
|
||||
return mcu_ipc_readl(id, EXYNOS_MCU_IPC_ISSR0 + (4 * mbx_num));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_get_value);
|
||||
|
||||
void mbox_set_value(enum mcu_ipc_region id, u32 mbx_num, u32 msg)
|
||||
{
|
||||
if (mbx_num < 64)
|
||||
mcu_ipc_writel(id, msg, EXYNOS_MCU_IPC_ISSR0 + (4 * mbx_num));
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_set_value);
|
||||
|
||||
u32 mbox_extract_value(enum mcu_ipc_region id, u32 mbx_num, u32 mask, u32 pos)
|
||||
{
|
||||
if (mbx_num < 64)
|
||||
return (mbox_get_value(id, mbx_num) >> pos) & mask;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_update_value);
|
||||
|
||||
void mbox_update_value(enum mcu_ipc_region id, u32 mbx_num,
|
||||
u32 msg, u32 mask, u32 pos)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mcu_dat[id].lock, flags);
|
||||
|
||||
if (mbx_num < 64) {
|
||||
val = mbox_get_value(id, mbx_num);
|
||||
val &= ~(mask << pos);
|
||||
val |= (msg & mask) << pos;
|
||||
mbox_set_value(id, mbx_num, val);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mcu_dat[id].lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_update_value);
|
||||
|
||||
void mbox_sw_reset(enum mcu_ipc_region id)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
printk("Reset Mailbox registers\n");
|
||||
|
||||
reg_val = mcu_ipc_readl(id, EXYNOS_MCU_IPC_MCUCTLR);
|
||||
reg_val |= (0x1 << MCU_IPC_MCUCTLR_MSWRST);
|
||||
|
||||
mcu_ipc_writel(id, reg_val, EXYNOS_MCU_IPC_MCUCTLR) ;
|
||||
|
||||
udelay(5);
|
||||
}
|
||||
EXPORT_SYMBOL(mbox_sw_reset);
|
||||
|
||||
static void mcu_ipc_clear_all_interrupt(enum mcu_ipc_region id)
|
||||
{
|
||||
mcu_ipc_writel(id, 0xFFFF, EXYNOS_MCU_IPC_INTCR1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARGOS
|
||||
static int mcu_ipc_set_affinity(enum mcu_ipc_region id, struct device *dev, int irq)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 irq_affinity_mask = 0;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "non-DT project, can't set irq affinity\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "mcu,irq_affinity_mask",
|
||||
&irq_affinity_mask)) {
|
||||
dev_err(dev, "Failed to get affinity mask\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(dev, "irq_affinity_mask = 0x%x\n", irq_affinity_mask);
|
||||
|
||||
if (!zalloc_cpumask_var(&mcu_dat[id].dmask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
if (!zalloc_cpumask_var(&mcu_dat[id].imask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
cpumask_or(mcu_dat[id].imask, mcu_dat[id].imask, cpumask_of(irq_affinity_mask));
|
||||
cpumask_copy(mcu_dat[id].dmask, get_default_cpu_mask());
|
||||
|
||||
return argos_irq_affinity_setup_label(irq, "IPC", mcu_dat[id].imask,
|
||||
mcu_dat[id].dmask);
|
||||
}
|
||||
#else
|
||||
static int mcu_ipc_set_affinity(enum mcu_ipc_region id, struct device *dev, int irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCU_IPC_TEST
|
||||
static void test_without_dev(enum mcu_ipc_region id)
|
||||
{
|
||||
int i;
|
||||
char *region;
|
||||
|
||||
switch(id) {
|
||||
case MCU_CP:
|
||||
region = "CP";
|
||||
break;
|
||||
case MCU_GNSS:
|
||||
region = "GNSS";
|
||||
break;
|
||||
default:
|
||||
region = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
mbox_set_value(id, i, 64 - i);
|
||||
mdelay(50);
|
||||
dev_err(mcu_dat[id].mcu_ipc_dev,
|
||||
"Test without %s(%d): Read mbox value[%d]: %d\n",
|
||||
region, id, i, mbox_get_value(i));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mcu_ipc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res = NULL;
|
||||
int mcu_ipc_irq;
|
||||
int err = 0;
|
||||
u32 id;
|
||||
|
||||
dev_err(&pdev->dev, "%s: mcu_ipc probe start.\n", __func__);
|
||||
|
||||
err = of_property_read_u32(dev->of_node, "mcu,id", &id);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "MCU IPC parse error! [id]\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (id >= MCU_MAX) {
|
||||
dev_err(&pdev->dev, "MCU IPC Invalid ID [%d]\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mcu_dat[id].id = id;
|
||||
mcu_dat[id].mcu_ipc_dev = &pdev->dev;
|
||||
|
||||
if (!pdev->dev.dma_mask)
|
||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
if (!pdev->dev.coherent_dma_mask)
|
||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
if (dev->of_node) {
|
||||
mcu_dt_read_string(dev->of_node, "mcu,name", mcu_dat[id].name);
|
||||
if (IS_ERR(&mcu_dat[id])) {
|
||||
dev_err(&pdev->dev, "MCU IPC parse error!\n");
|
||||
return PTR_ERR(&mcu_dat[id]);
|
||||
}
|
||||
}
|
||||
|
||||
/* resource for mcu_ipc SFR region */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mcu_dat[id].ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mcu_dat[id].ioaddr)) {
|
||||
dev_err(&pdev->dev, "failded to request memory resource\n");
|
||||
return PTR_ERR(mcu_dat[id].ioaddr);
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
mcu_ipc_irq = platform_get_irq(pdev, 0);
|
||||
err = devm_request_irq(&pdev->dev, mcu_ipc_irq, mcu_ipc_handler, 0,
|
||||
pdev->name, &mcu_dat[id]);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Can't request MCU_IPC IRQ\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
mcu_ipc_clear_all_interrupt(id);
|
||||
|
||||
/* set argos irq affinity */
|
||||
err = mcu_ipc_set_affinity(id, dev, mcu_ipc_irq);
|
||||
if (err)
|
||||
dev_err(dev, "Can't set IRQ affinity with(%d)\n", err);
|
||||
|
||||
#ifdef CONFIG_MCU_IPC_TEST
|
||||
test_without_dev(id);
|
||||
#endif
|
||||
|
||||
spin_lock_init(&mcu_dat[id].lock);
|
||||
|
||||
dev_err(&pdev->dev, "%s: mcu_ipc probe done.\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit mcu_ipc_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcu_ipc_suspend(struct device *dev)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcu_ipc_resume(struct device *dev)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define mcu_ipc_suspend NULL
|
||||
#define mcu_ipc_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mcu_ipc_pm_ops = {
|
||||
.suspend = mcu_ipc_suspend,
|
||||
.resume = mcu_ipc_resume,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_mcu_ipc_dt_match[] = {
|
||||
{ .compatible = "samsung,exynos7580-mailbox", },
|
||||
{ .compatible = "samsung,exynos7890-mailbox", },
|
||||
{ .compatible = "samsung,exynos8890-mailbox", },
|
||||
{ .compatible = "samsung,exynos7870-mailbox", },
|
||||
{ .compatible = "samsung,exynos-shd-ipc-mailbox", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_mcu_ipc_dt_match);
|
||||
|
||||
static struct platform_driver mcu_ipc_driver = {
|
||||
.probe = mcu_ipc_probe,
|
||||
.remove = mcu_ipc_remove,
|
||||
.driver = {
|
||||
.name = "mcu_ipc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(exynos_mcu_ipc_dt_match),
|
||||
.pm = &mcu_ipc_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mcu_ipc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MCU IPC driver");
|
||||
MODULE_AUTHOR("<hy50.seo@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
117
drivers/misc/mcu_ipc/mcu_ipc.h
Normal file
117
drivers/misc/mcu_ipc/mcu_ipc.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Samsung Electronics Co.Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* MCU IPC driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef MCU_IPC_H
|
||||
#define MCU_IPC_H
|
||||
|
||||
/* FIXME: will be removed */
|
||||
/* Shared register with 64 * 32 words */
|
||||
#define MAX_MBOX_NUM 64
|
||||
|
||||
enum mcu_ipc_region {
|
||||
MCU_CP,
|
||||
MCU_GNSS,
|
||||
MCU_MAX,
|
||||
};
|
||||
|
||||
struct mcu_ipc_ipc_handler {
|
||||
void *data;
|
||||
void (*handler)(void *);
|
||||
};
|
||||
|
||||
struct mcu_ipc_drv_data {
|
||||
char *name;
|
||||
u32 id;
|
||||
|
||||
void __iomem *ioaddr;
|
||||
u32 registered_irq;
|
||||
|
||||
/**
|
||||
* irq affinity cpu mask
|
||||
*/
|
||||
cpumask_var_t dmask; /* default cpu mask */
|
||||
cpumask_var_t imask; /* irq affinity cpu mask */
|
||||
|
||||
struct device *mcu_ipc_dev;
|
||||
struct mcu_ipc_ipc_handler hd[16];
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct mcu_ipc_drv_data mcu_dat[MCU_MAX];
|
||||
|
||||
static inline void mcu_ipc_writel(enum mcu_ipc_region id, u32 val, long reg)
|
||||
{
|
||||
writel(val, mcu_dat[id].ioaddr + reg);
|
||||
}
|
||||
|
||||
static inline u32 mcu_ipc_readl(enum mcu_ipc_region id, long reg)
|
||||
{
|
||||
return readl(mcu_dat[id].ioaddr + reg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARGOS
|
||||
/* kernel team needs to provide argos header file. !!!
|
||||
* As of now, there's nothing to use. */
|
||||
#ifdef CONFIG_SCHED_HMP
|
||||
extern struct cpumask hmp_slow_cpu_mask;
|
||||
extern struct cpumask hmp_fast_cpu_mask;
|
||||
|
||||
static inline struct cpumask *get_default_cpu_mask(void)
|
||||
{
|
||||
return &hmp_slow_cpu_mask;
|
||||
}
|
||||
#else
|
||||
static inline struct cpumask *get_default_cpu_mask(void)
|
||||
{
|
||||
return cpu_all_mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
int argos_irq_affinity_setup_label(unsigned int irq, const char *label,
|
||||
struct cpumask *affinity_cpu_mask,
|
||||
struct cpumask *default_cpu_mask);
|
||||
int argos_task_affinity_setup_label(struct task_struct *p, const char *label,
|
||||
struct cpumask *affinity_cpu_mask,
|
||||
struct cpumask *default_cpu_mask);
|
||||
#endif
|
||||
|
||||
#define mcu_dt_read_enum(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = (__typeof__(dest))(val); \
|
||||
} while (0)
|
||||
|
||||
#define mcu_dt_read_bool(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = val ? true : false; \
|
||||
} while (0)
|
||||
|
||||
#define mcu_dt_read_string(np, prop, dest) \
|
||||
do { \
|
||||
if (of_property_read_string(np, prop, \
|
||||
(const char **)&dest)) \
|
||||
return -EINVAL; \
|
||||
} while (0)
|
||||
|
||||
#define mcu_dt_read_u32(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = val; \
|
||||
} while (0)
|
||||
#endif
|
57
drivers/misc/mcu_ipc/regs-mcu_ipc.h
Normal file
57
drivers/misc/mcu_ipc/regs-mcu_ipc.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* Register definition file for Samsung MCU_IPC
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_REGS_MCU_IPC_H
|
||||
#define __ASM_ARCH_REGS_MCU_IPC_H
|
||||
|
||||
/***************************************************************/
|
||||
/* MCU_IPC Registers part */
|
||||
/***************************************************************/
|
||||
#define EXYNOS_MCU_IPC_MCUCTLR 0x0
|
||||
#define EXYNOS_MCU_IPC_INTGR0 0x8
|
||||
#define EXYNOS_MCU_IPC_INTCR0 0xc
|
||||
#define EXYNOS_MCU_IPC_INTMR0 0x10
|
||||
#define EXYNOS_MCU_IPC_INTSR0 0x14
|
||||
#define EXYNOS_MCU_IPC_INTMSR0 0x18
|
||||
#define EXYNOS_MCU_IPC_INTGR1 0x1c
|
||||
#define EXYNOS_MCU_IPC_INTCR1 0x20
|
||||
#define EXYNOS_MCU_IPC_INTMR1 0x24
|
||||
#define EXYNOS_MCU_IPC_INTSR1 0x28
|
||||
#define EXYNOS_MCU_IPC_INTMSR1 0x2c
|
||||
#define EXYNOS_MCU_IPC_ISSR0 0x80
|
||||
#define EXYNOS_MCU_IPC_ISSR1 0x84
|
||||
#define EXYNOS_MCU_IPC_ISSR2 0x88
|
||||
#define EXYNOS_MCU_IPC_ISSR3 0x8c
|
||||
|
||||
/***************************************************************/
|
||||
/* MCU_IPC Bit definition part */
|
||||
/***************************************************************/
|
||||
/* SYSREG Bit definition */
|
||||
#define MCU_IPC_MCUCTLR_MSWRST (0) /* MCUCTRL S/W Reset */
|
||||
|
||||
#define MCU_IPC_RX_INT0 (1 << 16)
|
||||
#define MCU_IPC_RX_INT1 (1 << 17)
|
||||
#define MCU_IPC_RX_INT2 (1 << 18)
|
||||
#define MCU_IPC_RX_INT3 (1 << 19)
|
||||
#define MCU_IPC_RX_INT4 (1 << 20)
|
||||
#define MCU_IPC_RX_INT5 (1 << 21)
|
||||
#define MCU_IPC_RX_INT6 (1 << 22)
|
||||
#define MCU_IPC_RX_INT7 (1 << 23)
|
||||
#define MCU_IPC_RX_INT8 (1 << 24)
|
||||
#define MCU_IPC_RX_INT9 (1 << 25)
|
||||
#define MCU_IPC_RX_INT10 (1 << 26)
|
||||
#define MCU_IPC_RX_INT11 (1 << 27)
|
||||
#define MCU_IPC_RX_INT12 (1 << 28)
|
||||
#define MCU_IPC_RX_INT13 (1 << 29)
|
||||
#define MCU_IPC_RX_INT14 (1 << 30)
|
||||
#define MCU_IPC_RX_INT15 (1 << 31)
|
||||
|
||||
#endif /* __ASM_ARCH_REGS_MCU_IPC_H */
|
242
drivers/misc/mcu_ipc/shm_ipc.c
Normal file
242
drivers/misc/mcu_ipc/shm_ipc.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Samsung Electronics Co.Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Shared memory driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/shm_ipc.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
struct shm_plat_data {
|
||||
unsigned long p_addr;
|
||||
unsigned t_size;
|
||||
unsigned ipc_off;
|
||||
unsigned ipc_size;
|
||||
|
||||
unsigned long p_sysram_addr;
|
||||
unsigned t_sysram_size;
|
||||
} pdata;
|
||||
|
||||
unsigned long shm_get_phys_base(void)
|
||||
{
|
||||
return pdata.p_addr;
|
||||
}
|
||||
|
||||
unsigned shm_get_phys_size(void)
|
||||
{
|
||||
return pdata.t_size;
|
||||
}
|
||||
|
||||
unsigned long shm_get_sysram_base(void)
|
||||
{
|
||||
return pdata.p_sysram_addr;
|
||||
}
|
||||
|
||||
unsigned shm_get_sysram_size(void)
|
||||
{
|
||||
return pdata.t_sysram_size;
|
||||
}
|
||||
|
||||
unsigned shm_get_boot_size(void)
|
||||
{
|
||||
return pdata.ipc_off;
|
||||
}
|
||||
|
||||
unsigned shm_get_ipc_rgn_offset(void)
|
||||
{
|
||||
return pdata.ipc_off;
|
||||
}
|
||||
|
||||
unsigned shm_get_ipc_rgn_size(void)
|
||||
{
|
||||
return pdata.ipc_size;
|
||||
}
|
||||
|
||||
unsigned long shm_get_security_param3(unsigned long mode, u32 main_size)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
switch (mode) {
|
||||
case 0: /* CP_BOOT_MODE_NORMAL */
|
||||
ret = main_size;
|
||||
break;
|
||||
case 1: /* CP_BOOT_MODE_DUMP */
|
||||
#ifdef CP_NONSECURE_BOOT
|
||||
ret = pdata.p_addr;
|
||||
#else
|
||||
ret = pdata.p_addr + pdata.ipc_off;
|
||||
#endif
|
||||
break;
|
||||
case 2: /* CP_BOOT_RE_INIT */
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: Invalid sec_mode(%lu)\n", __func__, mode);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long shm_get_security_param2(unsigned long mode, u32 bl_size)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
switch (mode) {
|
||||
case 0: /* CP_BOOT_MODE_NORMAL */
|
||||
case 1: /* CP_BOOT_MODE_DUMP */
|
||||
ret = bl_size;
|
||||
break;
|
||||
case 2: /* CP_BOOT_RE_INIT */
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: Invalid sec_mode(%lu)\n", __func__, mode);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __iomem *shm_request_region(unsigned long sh_addr, unsigned size)
|
||||
{
|
||||
int i;
|
||||
unsigned int num_pages = (size >> PAGE_SHIFT);
|
||||
pgprot_t prot = pgprot_writecombine(PAGE_KERNEL);
|
||||
struct page **pages;
|
||||
void *v_addr;
|
||||
|
||||
if (!sh_addr)
|
||||
return NULL;
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) * num_pages, GFP_ATOMIC);
|
||||
|
||||
for (i = 0; i < (num_pages); i++) {
|
||||
pages[i] = phys_to_page(sh_addr);
|
||||
sh_addr += PAGE_SIZE;
|
||||
}
|
||||
|
||||
v_addr = vmap(pages, num_pages, VM_MAP, prot);
|
||||
kfree(pages);
|
||||
|
||||
return (void __iomem *)v_addr;
|
||||
}
|
||||
|
||||
|
||||
void __iomem *shm_get_boot_region(void)
|
||||
{
|
||||
return shm_request_region(pdata.p_addr, pdata.ipc_off);
|
||||
}
|
||||
|
||||
void __iomem *shm_get_ipc_region(void)
|
||||
{
|
||||
return shm_request_region(pdata.p_addr + pdata.ipc_off,
|
||||
pdata.t_size - pdata.ipc_off);
|
||||
}
|
||||
|
||||
void shm_release_region(void *v_addr)
|
||||
{
|
||||
vunmap(v_addr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_RESERVED_MEM
|
||||
static int __init modem_if_reserved_mem_setup(struct reserved_mem *remem)
|
||||
{
|
||||
pdata.p_addr = remem->base;
|
||||
pdata.t_size = remem->size;
|
||||
|
||||
pr_err("%s: memory reserved: paddr=%lu, t_size=%u\n",
|
||||
__func__, pdata.p_addr, pdata.t_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RESERVEDMEM_OF_DECLARE(modem_if, "exynos,modem_if", modem_if_reserved_mem_setup);
|
||||
|
||||
#if !defined (CONFIG_SOC_EXYNOS7570)
|
||||
static int __init deliver_cp_reserved_mem_setup(struct reserved_mem *remem)
|
||||
{
|
||||
pdata.p_sysram_addr = remem->base;
|
||||
pdata.t_sysram_size = remem->size;
|
||||
|
||||
pr_err("%s: memory reserved: paddr=%u, t_size=%u\n",
|
||||
__func__, (u32)remem->base, (u32)remem->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
RESERVEDMEM_OF_DECLARE(deliver_cp, "exynos,deliver_cp", deliver_cp_reserved_mem_setup);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static int shm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
dev_err(dev, "%s: shmem driver init\n", __func__);
|
||||
|
||||
if (dev->of_node) {
|
||||
ret = of_property_read_u32(dev->of_node, "shmem,ipc_offset",
|
||||
&pdata.ipc_off);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get property, ipc_offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "shmem,ipc_size",
|
||||
&pdata.ipc_size);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get property, ipc_size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* To do: In case of non-DT */
|
||||
}
|
||||
|
||||
dev_err(dev, "paddr=%lu, t_size=%u, ipc_off=%u, ipc_size=%u\n",
|
||||
pdata.p_addr, pdata.t_size, pdata.ipc_off, pdata.ipc_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shm_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos_shm_dt_match[] = {
|
||||
{ .compatible = "samsung,exynos7580-shm_ipc", },
|
||||
{ .compatible = "samsung,exynos8890-shm_ipc", },
|
||||
{ .compatible = "samsung,exynos7870-shm_ipc", },
|
||||
{ .compatible = "samsung,exynos-shm_ipc", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_shm_dt_match);
|
||||
|
||||
static struct platform_driver shmem_driver = {
|
||||
.probe = shm_probe,
|
||||
.remove = shm_remove,
|
||||
.driver = {
|
||||
.name = "shm_ipc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(exynos_shm_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(shmem_driver);
|
||||
|
||||
MODULE_DESCRIPTION("");
|
||||
MODULE_AUTHOR("");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue