blob: c98a1b9586d2d89b8603f9f5e78036b1e7b2ea6f [file] [log] [blame]
/*
* Remote processor resource manager
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2011 Google, Inc.
*
* Fernando Guzman Lugo <fernando.lugo@ti.com>
* Miguel Vadillo <vadillo@ti.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/slab.h>
#include <linux/rpmsg.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/remoteproc.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/debugfs.h>
#include <linux/rpmsg_resmgr.h>
#include <linux/pm_runtime.h>
#include <plat/dmtimer.h>
#include <plat/rpres.h>
#include <plat/clock.h>
#include <plat/dma.h>
#include <plat/i2c.h>
#include <plat/omap_hwmod.h>
#define NAME_SIZE 50
#define REGULATOR_MAX 1
#define NUM_SRC_CLK 3
#define AUX_CLK_MIN 0
#define AUX_CLK_MAX 5
#define GPTIMERS_MAX 11
#define MHZ 1000000
#define MAX_MSG (sizeof(struct rprm_ack) + sizeof(struct rprm_sdma))
static struct dentry *rprm_dbg;
static char *regulator_name[] = {
"cam2pwr"
};
static char *clk_src_name[] = {
"sys_clkin_ck",
"dpll_core_m3x2_ck",
"dpll_per_m3x2_ck",
};
static const char const *rnames[] = {
[RPRM_GPTIMER] = "GP Timer",
[RPRM_L3BUS] = "L3 bus",
[RPRM_IVAHD] = "IVA HD",
[RPRM_IVASEQ0] = "IVA SEQ0",
[RPRM_IVASEQ1] = "IVA SEQ1",
[RPRM_ISS] = "ISS",
[RPRM_SL2IF] = "SL2IF",
[RPRM_FDIF] = "FDIF",
[RPRM_AUXCLK] = "AUXCLK",
[RPRM_REGULATOR] = "REGULATOR",
[RPRM_GPIO] = "GPIO",
[RPRM_SDMA] = "SDMA",
[RPRM_IPU] = "IPU",
[RPRM_DSP] = "DSP",
[RPRM_I2C] = "I2C",
};
static const char *rname(u32 type) {
if (type >= RPRM_MAX)
return "(invalid)";
return rnames[type];
}
struct rprm_elem {
struct list_head next;
u32 src;
u32 type;
u32 id;
void *handle;
u32 base;
struct rprm_constraints_data *constraints;
char res[];
};
struct rprm {
struct list_head res_list;
struct idr conn_list;
struct idr id_list;
struct mutex lock;
struct dentry *dbg_dir;
};
struct rprm_auxclk_depot {
struct clk *aux_clk;
struct clk *src;
};
struct rprm_regulator_depot {
struct regulator *reg_p;
u32 orig_uv;
};
static struct rprm_constraints_data def_data = {
.frequency = 0,
.bandwidth = -1,
.latency = -1,
};
static int _get_rprm_size(u32 type)
{
switch (type) {
case RPRM_GPTIMER:
return sizeof(struct rprm_gpt);
case RPRM_AUXCLK:
return sizeof(struct rprm_auxclk);
case RPRM_REGULATOR:
return sizeof(struct rprm_regulator);
case RPRM_GPIO:
return sizeof(struct rprm_gpio);
case RPRM_SDMA:
return sizeof(struct rprm_sdma);
case RPRM_I2C:
return sizeof(struct rprm_i2c);
}
return 0;
}
static int rprm_gptimer_request(struct rprm_elem *e, struct rprm_gpt *obj)
{
int ret;
struct omap_dm_timer *gpt;
if (obj->id > GPTIMERS_MAX) {
pr_err("Invalid gptimer %u\n", obj->id);
return -EINVAL;
}
gpt = omap_dm_timer_request_specific(obj->id);
if (!gpt)
return -EBUSY;
ret = omap_dm_timer_set_source(gpt, obj->src_clk);
if (!ret)
e->handle = gpt;
else
omap_dm_timer_free(gpt);
return ret;
}
static void rprm_gptimer_release(struct omap_dm_timer *obj)
{
omap_dm_timer_free(obj);
}
static int rprm_auxclk_request(struct rprm_elem *e, struct rprm_auxclk *obj)
{
int ret;
char clk_name[NAME_SIZE];
char src_clk_name[NAME_SIZE];
struct rprm_auxclk_depot *acd;
struct clk *src_parent;
if ((obj->id < AUX_CLK_MIN) || (obj->id > AUX_CLK_MAX)) {
pr_err("Invalid aux_clk %d\n", obj->id);
return -EINVAL;
}
/* Create auxclks depot */
acd = kmalloc(sizeof(*acd), GFP_KERNEL);
if (!acd)
return -ENOMEM;
sprintf(clk_name, "auxclk%d_ck", obj->id);
acd->aux_clk = clk_get(NULL, clk_name);
if (!acd->aux_clk) {
pr_err("%s: unable to get clock %s\n", __func__, clk_name);
ret = -EIO;
goto error;
}
if (unlikely(acd->aux_clk->usecount))
pr_warn("There are other users of %d clk\n", obj->id);
sprintf(src_clk_name, "auxclk%d_src_ck", obj->id);
acd->src = clk_get(NULL, src_clk_name);
if (!acd->src) {
pr_err("%s: unable to get clock %s\n", __func__, src_clk_name);
ret = -EIO;
goto error_aux;
}
src_parent = clk_get(NULL, clk_src_name[obj->parent_src_clk]);
if (!src_parent) {
pr_err("%s: unable to get parent clock %s\n", __func__,
clk_src_name[obj->parent_src_clk]);
ret = -EIO;
goto error_aux_src;
}
ret = clk_set_rate(src_parent, (obj->parent_src_clk_rate * MHZ));
if (ret) {
pr_err("%s: rate not supported by %s\n", __func__,
clk_src_name[obj->parent_src_clk]);
goto error_aux_src_parent;
}
ret = clk_set_parent(acd->src, src_parent);
if (ret) {
pr_err("%s: unable to set clk %s as parent of aux_clk %s\n",
__func__,
clk_src_name[obj->parent_src_clk],
src_clk_name);
goto error_aux_src_parent;
}
ret = clk_enable(acd->src);
if (ret) {
pr_err("%s: error enabling %s\n", __func__, src_clk_name);
goto error_aux_src_parent;
}
ret = clk_set_rate(acd->aux_clk, (obj->clk_rate * MHZ));
if (ret) {
pr_err("%s: rate not supported by %s\n", __func__, clk_name);
goto error_aux_enable;
}
ret = clk_enable(acd->aux_clk);
if (ret) {
pr_err("%s: error enabling %s\n", __func__, clk_name);
goto error_aux_enable;
}
clk_put(src_parent);
e->handle = acd;
return 0;
error_aux_enable:
clk_disable(acd->src);
error_aux_src_parent:
clk_put(src_parent);
error_aux_src:
clk_put(acd->src);
error_aux:
clk_put(acd->aux_clk);
error:
kfree(acd);
return ret;
}
static void rprm_auxclk_release(struct rprm_auxclk_depot *obj)
{
clk_disable((struct clk *)obj->aux_clk);
clk_put((struct clk *)obj->aux_clk);
clk_disable((struct clk *)obj->src);
clk_put((struct clk *)obj->src);
kfree(obj);
}
static
int rprm_regulator_request(struct rprm_elem *e, struct rprm_regulator *obj)
{
int ret;
struct rprm_regulator_depot *rd;
char *reg_name;
if (obj->id > REGULATOR_MAX) {
pr_err("Invalid regulator %d\n", obj->id);
return -EINVAL;
}
/* Create regulator depot */
rd = kmalloc(sizeof(*rd), GFP_KERNEL);
if (!rd)
return -ENOMEM;
reg_name = regulator_name[obj->id - 1];
rd->reg_p = regulator_get_exclusive(NULL, reg_name);
if (IS_ERR_OR_NULL(rd->reg_p)) {
pr_err("%s: error providing regulator %s\n", __func__, reg_name);
ret = -EINVAL;
goto error;
}
rd->orig_uv = regulator_get_voltage(rd->reg_p);
ret = regulator_set_voltage(rd->reg_p, obj->min_uv, obj->max_uv);
if (ret) {
pr_err("%s: error setting %s voltage\n", __func__, reg_name);
goto error_reg;
}
ret = regulator_enable(rd->reg_p);
if (ret) {
pr_err("%s: error enabling %s ldo\n", __func__, reg_name);
goto error_reg;
}
e->handle = rd;
return 0;
error_reg:
regulator_put(rd->reg_p);
error:
kfree(rd);
return ret;
}
static void rprm_regulator_release(struct rprm_regulator_depot *obj)
{
int ret;
ret = regulator_disable(obj->reg_p);
if (ret) {
pr_err("%s: error disabling ldo\n", __func__);
return;
}
/* Restore orginal voltage */
ret = regulator_set_voltage(obj->reg_p, obj->orig_uv, obj->orig_uv);
if (ret) {
pr_err("%s: error restoring voltage\n", __func__);
return;
}
regulator_put(obj->reg_p);
kfree(obj);
}
static int rprm_gpio_request(struct rprm_elem *e, struct rprm_gpio *obj)
{
int ret;
struct rprm_gpio *gd;
/* Create gpio depot */
gd = kmalloc(sizeof(*gd), GFP_KERNEL);
if (!gd)
return -ENOMEM;
ret = gpio_request(obj->id , "rpmsg_resmgr");
if (ret) {
pr_err("%s: error providing gpio %d\n", __func__, obj->id);
return ret;
}
e->handle = memcpy(gd, obj, sizeof(*obj));
return ret;
}
static void rprm_gpio_release(struct rprm_gpio *obj)
{
gpio_free(obj->id);
kfree(obj);
}
static int rprm_sdma_request(struct rprm_elem *e, struct rprm_sdma *obj)
{
int ret;
int sdma;
int i;
struct rprm_sdma *sd;
/* Create sdma depot */
sd = kmalloc(sizeof(*sd), GFP_KERNEL);
if (!sd)
return -ENOMEM;
if (obj->num_chs > MAX_NUM_SDMA_CHANNELS) {
pr_err("Not able to provide %u channels\n", obj->num_chs);
return -EINVAL;
}
for (i = 0; i < obj->num_chs; i++) {
ret = omap_request_dma(0, "rpmsg_resmgr", NULL, NULL, &sdma);
if (ret) {
pr_err("Error providing sdma channel %d\n", ret);
goto err;
}
obj->channels[i] = sdma;
pr_debug("Providing sdma ch %d\n", sdma);
}
e->handle = memcpy(sd, obj, sizeof(*obj));
return 0;
err:
while (i--)
omap_free_dma(obj->channels[i]);
kfree(sd);
return ret;
}
static void rprm_sdma_release(struct rprm_sdma *obj)
{
int i = obj->num_chs;
while (i--) {
omap_free_dma(obj->channels[i]);
pr_debug("Releasing sdma ch %d\n", obj->channels[i]);
}
kfree(obj);
}
static int rprm_i2c_request(struct rprm_elem *e, struct rprm_i2c *obj)
{
struct device *i2c_dev;
struct i2c_adapter *adapter;
char i2c_name[NAME_SIZE];
int ret = -EINVAL;
sprintf(i2c_name, "i2c%d", obj->id);
i2c_dev = omap_hwmod_name_get_dev(i2c_name);
if (IS_ERR_OR_NULL(i2c_dev)) {
pr_err("%s: unable to lookup %s\n", __func__, i2c_name);
return ret;
}
adapter = i2c_get_adapter(obj->id);
if (!adapter) {
pr_err("%s: could not get i2c%d adapter\n", __func__, obj->id);
return -EINVAL;
}
i2c_detect_ext_master(adapter);
i2c_put_adapter(adapter);
ret = pm_runtime_get_sync(i2c_dev);
/*
* pm_runtime_get_sync can return 1 in case it is already active,
* change it to 0 to indicate success.
*/
ret -= ret == 1;
if (!ret)
e->handle = i2c_dev;
else
dev_warn(i2c_dev, "%s: failed get sync %d\n", __func__, ret);
return ret;
}
static int rprm_i2c_release(struct device *i2c_dev)
{
int ret = -EINVAL;
if (IS_ERR_OR_NULL(i2c_dev)) {
pr_err("%s: invalid device passed\n", __func__);
return ret;
}
ret = pm_runtime_put_sync(i2c_dev);
if (ret)
dev_warn(i2c_dev, "%s: failed put sync %d\n", __func__, ret);
return ret;
}
static const char *_get_rpres_name(int type)
{
switch (type) {
case RPRM_IVAHD:
return "rpres_iva";
case RPRM_IVASEQ0:
return "rpres_iva_seq0";
case RPRM_IVASEQ1:
return "rpres_iva_seq1";
case RPRM_ISS:
return "rpres_iss";
case RPRM_FDIF:
return "rpres_fdif";
case RPRM_SL2IF:
return "rpres_sl2if";
}
return "";
}
static int _rpres_set_constraints(struct rprm_elem *e, u32 type, long val)
{
switch (type) {
case RPRM_SCALE:
return rpres_set_constraints(e->handle,
RPRES_CONSTRAINT_SCALE,
val);
case RPRM_LATENCY:
return rpres_set_constraints(e->handle,
RPRES_CONSTRAINT_LATENCY,
val);
case RPRM_BANDWIDTH:
return rpres_set_constraints(e->handle,
RPRES_CONSTRAINT_BANDWIDTH,
val);
}
pr_err("Invalid constraint\n");
return -EINVAL;
}
static int _rproc_set_constraints(struct rprm_elem *e, u32 type, long val)
{
switch (type) {
case RPRM_SCALE:
return rproc_set_constraints(e->handle,
RPROC_CONSTRAINT_SCALE,
val);
case RPRM_LATENCY:
return rproc_set_constraints(e->handle,
RPROC_CONSTRAINT_LATENCY,
val);
case RPRM_BANDWIDTH:
return rproc_set_constraints(e->handle,
RPROC_CONSTRAINT_BANDWIDTH,
val);
}
pr_err("Invalid constraint\n");
return -EINVAL;
}
static
int _set_constraints(struct rprm_elem *e, struct rprm_constraints_data *c)
{
int ret = -EINVAL;
u32 mask = 0;
int (*_set_constraints_func)(struct rprm_elem *, u32 type, long val);
switch (e->type) {
case RPRM_IVAHD:
case RPRM_ISS:
case RPRM_FDIF:
_set_constraints_func = _rpres_set_constraints;
break;
case RPRM_IPU:
_set_constraints_func = _rproc_set_constraints;
break;
default:
return -EINVAL;
}
if (c->mask & RPRM_SCALE) {
ret = _set_constraints_func(e, RPRM_SCALE, c->frequency);
if (ret)
goto err;
mask |= RPRM_SCALE;
e->constraints->frequency = c->frequency;
}
if (c->mask & RPRM_LATENCY) {
ret = _set_constraints_func(e, RPRM_LATENCY, c->latency);
if (ret)
goto err;
mask |= RPRM_LATENCY;
e->constraints->latency = c->latency;
}
if (c->mask & RPRM_BANDWIDTH) {
ret = _set_constraints_func(e, RPRM_BANDWIDTH, c->bandwidth);
if (ret)
goto err;
mask |= RPRM_BANDWIDTH;
e->constraints->bandwidth = c->bandwidth;
}
err:
c->mask = mask;
return ret;
}
static int rprm_set_constraints(struct rprm *rprm, u32 addr, int res_id,
void *data, bool set)
{
int ret = 0;
struct rprm_elem *e;
mutex_lock(&rprm->lock);
if (!idr_find(&rprm->conn_list, addr)) {
ret = -ENOTCONN;
goto out;
}
e = idr_find(&rprm->id_list, res_id);
if (!e || e->src != addr) {
ret = -ENOENT;
goto out;
}
if (!e->constraints) {
pr_warn("No constraints\n");
ret = -EINVAL;
goto out;
}
if (set) {
ret = _set_constraints(e, data);
if (!ret) {
e->constraints->mask |=
((struct rprm_constraints_data *)data)->mask;
goto out;
}
}
def_data.mask = ((struct rprm_constraints_data *)data)->mask;
if (def_data.mask) {
_set_constraints(e, &def_data);
e->constraints->mask &=
~((struct rprm_constraints_data *)data)->mask;
}
out:
mutex_unlock(&rprm->lock);
return ret;
}
static int rprm_rpres_request(struct rprm_elem *e, int type)
{
const char *res_name = _get_rpres_name(type);
struct rpres *res;
e->constraints = kzalloc(sizeof(*(e->constraints)), GFP_KERNEL);
if (!(e->constraints))
return -ENOMEM;
res = rpres_get(res_name);
if (IS_ERR(res)) {
pr_err("%s: error requesting %s\n", __func__, res_name);
kfree(e->constraints);
return PTR_ERR(res);
}
e->handle = res;
return 0;
}
static void rprm_rpres_release(struct rpres *res)
{
rpres_put(res);
}
static int rprm_rproc_request(struct rprm_elem *e, char *name)
{
struct rproc *rp;
e->constraints = kzalloc(sizeof(*(e->constraints)), GFP_KERNEL);
if (!(e->constraints))
return -ENOMEM;
rp = rproc_get(name);
if (IS_ERR(rp)) {
pr_debug("Error requesting %s\n", name);
kfree(e->constraints);
return PTR_ERR(rp);
}
e->handle = rp;
return 0;
}
static void rprm_rproc_release(struct rproc *rp)
{
rproc_put(rp);
}
static int _resource_free(struct rprm_elem *e)
{
int ret = 0;
if (e->constraints && e->constraints->mask) {
def_data.mask = e->constraints->mask;
_set_constraints(e, &def_data);
}
kfree(e->constraints);
switch (e->type) {
case RPRM_GPTIMER:
rprm_gptimer_release(e->handle);
break;
case RPRM_IVAHD:
case RPRM_IVASEQ0:
case RPRM_IVASEQ1:
case RPRM_ISS:
case RPRM_SL2IF:
case RPRM_FDIF:
rprm_rpres_release(e->handle);
break;
case RPRM_IPU:
case RPRM_DSP:
rprm_rproc_release(e->handle);
break;
case RPRM_AUXCLK:
rprm_auxclk_release(e->handle);
break;
case RPRM_I2C:
ret = rprm_i2c_release(e->handle);
break;
case RPRM_REGULATOR:
rprm_regulator_release(e->handle);
break;
case RPRM_GPIO:
rprm_gpio_release(e->handle);
break;
case RPRM_SDMA:
rprm_sdma_release(e->handle);
break;
case RPRM_L3BUS:
/* ignore silently */
break;
default:
return -EINVAL;
}
return ret;
}
static int rprm_resource_free(struct rprm *rprm, u32 addr, int res_id)
{
int ret = 0;
struct rprm_elem *e;
mutex_lock(&rprm->lock);
if (!idr_find(&rprm->conn_list, addr)) {
ret = -ENOTCONN;
goto out;
}
e = idr_find(&rprm->id_list, res_id);
if (!e || e->src != addr) {
ret = -ENOENT;
goto out;
}
idr_remove(&rprm->id_list, res_id);
list_del(&e->next);
out:
mutex_unlock(&rprm->lock);
if (!ret) {
ret = _resource_free(e);
kfree(e);
}
return ret;
}
static int _resource_alloc(struct rprm_elem *e, int type, void *data)
{
int ret = 0;
switch (type) {
case RPRM_GPTIMER:
ret = rprm_gptimer_request(e, data);
break;
case RPRM_IVAHD:
case RPRM_IVASEQ0:
case RPRM_IVASEQ1:
case RPRM_ISS:
case RPRM_SL2IF:
case RPRM_FDIF:
ret = rprm_rpres_request(e, type);
break;
case RPRM_IPU:
ret = rprm_rproc_request(e, "ipu");
break;
case RPRM_DSP:
ret = rprm_rproc_request(e, "dsp");
break;
case RPRM_AUXCLK:
ret = rprm_auxclk_request(e, data);
break;
case RPRM_I2C:
ret = rprm_i2c_request(e, data);
break;
case RPRM_REGULATOR:
ret = rprm_regulator_request(e, data);
break;
case RPRM_GPIO:
ret = rprm_gpio_request(e, data);
break;
case RPRM_SDMA:
ret = rprm_sdma_request(e, data);
break;
case RPRM_L3BUS:
/* ignore silently; */
break;
default:
pr_err("%s: invalid source %d!\n", __func__, type);
ret = -EINVAL;
}
return ret;
}
static int rprm_resource_alloc(struct rprm *rprm, u32 addr, int *res_id,
int type, void *data)
{
struct rprm_elem *e;
int ret;
int rlen = _get_rprm_size(type);
e = kzalloc(sizeof(*e) + rlen, GFP_KERNEL);
if (!e)
return -ENOMEM;
ret = _resource_alloc(e, type, data);
if (ret) {
pr_err("%s: request for %d (%s) failed: %d\n", __func__,
type, rname(type), ret);
goto err_res_alloc;
}
mutex_lock(&rprm->lock);
if (!idr_find(&rprm->conn_list, addr)) {
pr_err("%s: addr %d not connected!\n", __func__, addr);
ret = -ENOTCONN;
goto err;
}
/*
* Create a resource id to avoid sending kernel address to the
* remote processor.
*/
if (!idr_pre_get(&rprm->id_list, GFP_KERNEL)) {
ret = -ENOMEM;
goto err;
}
ret = idr_get_new(&rprm->id_list, e, res_id);
if (ret)
goto err;
e->type = type;
e->src = addr;
e->id = *res_id;
memcpy(e->res, data, rlen);
list_add(&e->next, &rprm->res_list);
mutex_unlock(&rprm->lock);
return 0;
err:
mutex_unlock(&rprm->lock);
_resource_free(e);
err_res_alloc:
kfree(e);
return ret;
}
static int rprm_disconnect_client(struct rprm *rprm, u32 addr)
{
struct rprm_elem *e, *tmp;
int ret;
mutex_lock(&rprm->lock);
if (!idr_find(&rprm->conn_list, addr)) {
ret = -ENOTCONN;
goto out;
}
list_for_each_entry_safe(e, tmp, &rprm->res_list, next) {
if (e->src == addr) {
_resource_free(e);
idr_remove(&rprm->id_list, e->id);
list_del(&e->next);
kfree(e);
}
}
idr_remove(&rprm->conn_list, addr);
out:
mutex_unlock(&rprm->lock);
return 0;
}
static int rpmsg_connect_client(struct rprm *rprm, u32 addr)
{
int ret;
int tid;
mutex_lock(&rprm->lock);
if (idr_find(&rprm->conn_list, addr)) {
pr_err("Connection already opened\n");
ret = -EISCONN;
goto out;
}
if (!idr_pre_get(&rprm->conn_list, GFP_KERNEL)) {
ret = -ENOMEM;
goto out;
}
ret = idr_get_new_above(&rprm->conn_list, &rprm->res_list, addr, &tid);
BUG_ON(addr != tid);
out:
mutex_unlock(&rprm->lock);
return ret;
}
static void rprm_cb(struct rpmsg_channel *rpdev, void *data, int len,
void *priv, u32 src)
{
int ret;
struct device *dev = &rpdev->dev;
struct rprm *rprm = dev_get_drvdata(dev);
struct rprm_request *req = data;
char ack_msg[MAX_MSG];
struct rprm_ack *ack = (void *)ack_msg;
int r_sz = 0;
if (len < sizeof(*req)) {
dev_err(dev, "Bad message\n");
return;
}
dev_dbg(dev, "resource type %d\n"
"request type %d\n"
"res_id %d",
req->res_type, req->acquire, req->res_id);
switch (req->acquire) {
case RPRM_CONNECT:
ret = rpmsg_connect_client(rprm, src);
if (ret)
dev_err(dev, "connection failed! ret %d\n", ret);
break;
case RPRM_REQ_ALLOC:
r_sz = len - sizeof(*req);
if (r_sz != _get_rprm_size(req->res_type)) {
r_sz = 0;
ret = -EINVAL;
break;
}
ret = rprm_resource_alloc(rprm, src, &req->res_id,
req->res_type, req->data);
if (ret)
dev_err(dev, "resource allocation failed! ret %d\n",
ret);
break;
case RPRM_REQ_FREE:
ret = rprm_resource_free(rprm, src, req->res_id);
if (ret)
dev_err(dev, "resource release failed! ret %d\n", ret);
return;
case RPRM_DISCONNECT:
ret = rprm_disconnect_client(rprm, src);
if (ret)
dev_err(dev, "disconnection failed ret %d\n", ret);
return;
case RPRM_REQ_CONSTRAINTS:
r_sz = len - sizeof(*req);
if (r_sz != sizeof(struct rprm_constraints_data)) {
r_sz = 0;
ret = -EINVAL;
break;
}
ret = rprm_set_constraints(rprm, src, req->res_id,
req->data, true);
if (ret)
dev_err(dev, "set constraints failed! ret %d\n", ret);
break;
case RPRM_REL_CONSTRAINTS:
ret = rprm_set_constraints(rprm, src, req->res_id,
req->data, false);
if (ret)
dev_err(dev, "rel constraints failed! ret %d\n", ret);
return;
default:
dev_err(dev, "Unknow request\n");
ret = -EINVAL;
}
ack->ret = ret;
ack->res_type = req->res_type;
ack->res_id = req->res_id;
memcpy(ack->data, req->data, r_sz);
ret = rpmsg_sendto(rpdev, ack, sizeof(*ack) + r_sz, src);
if (ret)
dev_err(dev, "rprm ack failed: %d\n", ret);
}
static int _printf_gptimer_args(char *buf, struct rprm_gpt *obj)
{
return sprintf(buf,
"Id:%d\n"
"Source:%d\n",
obj->id, obj->src_clk);
}
static int _printf_auxclk_args(char *buf, struct rprm_auxclk *obj)
{
return sprintf(buf,
"Id:%d\n"
"Rate:%2d\n"
"ParentSrc:%d\n"
"ParentSrcRate:%d\n",
obj->id, obj->clk_rate, obj->parent_src_clk,
obj->parent_src_clk_rate);
}
static int _printf_regulator_args(char *buf, struct rprm_regulator *obj)
{
return sprintf(buf,
"Id:%d\n"
"min_uV:%d\n"
"max_uV:%d\n",
obj->id, obj->min_uv, obj->max_uv);
}
static int _printf_gpio_args(char *buf, struct rprm_gpio *obj)
{
return sprintf(buf, "Id:%d\n", obj->id);
}
static int _printf_i2c_args(char *buf, struct rprm_i2c *obj)
{
return sprintf(buf, "Id:%d\n", obj->id);
}
static int _printf_sdma_args(char *buf, struct rprm_sdma *obj)
{
int i, ret = 0;
ret += sprintf(buf, "NumChannels:%d\n", obj->num_chs);
for (i = 0 ; i < obj->num_chs; i++)
ret += sprintf(buf + ret, "Channel[%d]:%d\n", i,
obj->channels[i]);
return ret;
}
static int _print_res_args(char *buf, struct rprm_elem *e)
{
void *res = (void *)e->res;
switch (e->type) {
case RPRM_GPTIMER:
return _printf_gptimer_args(buf, res);
case RPRM_AUXCLK:
return _printf_auxclk_args(buf, res);
case RPRM_I2C:
return _printf_i2c_args(buf, res);
case RPRM_REGULATOR:
return _printf_regulator_args(buf, res);
case RPRM_GPIO:
return _printf_gpio_args(buf, res);
case RPRM_SDMA:
return _printf_sdma_args(buf, res);
}
return 0;
}
static int _printf_constraints_args(char *buf, struct rprm_elem *e)
{
return sprintf(buf,
"Mask:0x%x\n"
"Frequency:%ld\n"
"Latency:%ld\n"
"Bandwidth:%ld\n",
e->constraints->mask, e->constraints->frequency,
e->constraints->latency, e->constraints->bandwidth);
}
static ssize_t rprm_dbg_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct rprm *rprm = filp->private_data;
struct rprm_elem *e;
char res[512];
int total = 0, c, tmp;
loff_t p = 0, pt;
list_for_each_entry(e, &rprm->res_list, next) {
c = sprintf(res,
"\nResource Name:%s\n"
"Source address:%d\n",
rnames[e->type], e->src);
if (_get_rprm_size(e->type))
c += _print_res_args(res + c, e);
if (e->constraints && e->constraints->mask)
c += _printf_constraints_args(res + c, e);
p += c;
if (*ppos >= p)
continue;
pt = c - p + *ppos;
tmp = simple_read_from_buffer(userbuf + total, count, &pt,
res, c);
total += tmp;
*ppos += tmp;
if (tmp - c)
break;
}
return total;
}
static int rprm_dbg_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static const struct file_operations rprm_dbg_ops = {
.read = rprm_dbg_read,
.open = rprm_dbg_open,
.llseek = generic_file_llseek,
};
static int rprm_probe(struct rpmsg_channel *rpdev)
{
struct rprm *rprm;
rprm = kmalloc(sizeof(*rprm), GFP_KERNEL);
if (!rprm)
return -ENOMEM;
mutex_init(&rprm->lock);
INIT_LIST_HEAD(&rprm->res_list);
idr_init(&rprm->conn_list);
idr_init(&rprm->id_list);
dev_set_drvdata(&rpdev->dev, rprm);
rprm->dbg_dir = debugfs_create_dir(dev_name(&rpdev->dev), rprm_dbg);
if (!rprm->dbg_dir)
dev_err(&rpdev->dev, "can't create debugfs dir\n");
debugfs_create_file("resources", 0400, rprm->dbg_dir, rprm,
&rprm_dbg_ops);
return 0;
}
static void __devexit rprm_remove(struct rpmsg_channel *rpdev)
{
struct rprm *rprm = dev_get_drvdata(&rpdev->dev);
struct rprm_elem *e, *tmp;
dev_info(&rpdev->dev, "Enter %s\n", __func__);
if (rprm->dbg_dir)
debugfs_remove_recursive(rprm->dbg_dir);
mutex_lock(&rprm->lock);
/* clean up remaining resources */
list_for_each_entry_safe(e, tmp, &rprm->res_list, next) {
_resource_free(e);
list_del(&e->next);
kfree(e);
}
idr_remove_all(&rprm->id_list);
idr_destroy(&rprm->id_list);
idr_remove_all(&rprm->conn_list);
idr_destroy(&rprm->conn_list);
mutex_unlock(&rprm->lock);
kfree(rprm);
}
static struct rpmsg_device_id rprm_id_table[] = {
{
.name = "rpmsg-resmgr",
},
{ },
};
MODULE_DEVICE_TABLE(platform, rprm_id_table);
static struct rpmsg_driver rprm_driver = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = rprm_id_table,
.probe = rprm_probe,
.callback = rprm_cb,
.remove = __devexit_p(rprm_remove),
};
static int __init init(void)
{
int r;
if (debugfs_initialized()) {
rprm_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (!rprm_dbg)
pr_err("Error creating rprm debug directory\n");
}
r = register_rpmsg_driver(&rprm_driver);
if (r && rprm_dbg)
debugfs_remove_recursive(rprm_dbg);
return r;
}
static void __exit fini(void)
{
if (rprm_dbg)
debugfs_remove_recursive(rprm_dbg);
unregister_rpmsg_driver(&rprm_driver);
}
module_init(init);
module_exit(fini);
MODULE_DESCRIPTION("Remote Processor Resource Manager");
MODULE_LICENSE("GPL v2");