diff options
Diffstat (limited to 'drivers/rpmsg/rpmsg_resmgr.c')
-rw-r--r-- | drivers/rpmsg/rpmsg_resmgr.c | 1211 |
1 files changed, 1211 insertions, 0 deletions
diff --git a/drivers/rpmsg/rpmsg_resmgr.c b/drivers/rpmsg/rpmsg_resmgr.c new file mode 100644 index 0000000..c98a1b9 --- /dev/null +++ b/drivers/rpmsg/rpmsg_resmgr.c @@ -0,0 +1,1211 @@ +/* + * 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"); |