satip-axe/kernel/drivers/stm/device.c

237 lines
5.0 KiB
C

/*
* (c) 2010 STMicroelectronics Limited
*
* Author: Stuart Menefy <stuart.menefy@st.com>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/stm/device.h>
struct stm_device_state {
struct device *dev;
struct stm_device_config *config;
enum stm_device_power_state power_state;
struct stm_pad_state *pad_state;
struct sysconf_field *sysconf_fields[0]; /* To be expanded */
};
static int __stm_device_init(struct stm_device_state *state,
struct stm_device_config *config, struct device *dev)
{
int i;
state->dev = dev;
state->config = config;
state->power_state = stm_device_power_off;
for (i = 0; i < config->sysconfs_num; i++) {
struct stm_device_sysconf *sysconf = &config->sysconfs[i];
state->sysconf_fields[i] = sysconf_claim(sysconf->regtype,
sysconf->regnum, sysconf->lsb, sysconf->msb,
dev_name(dev));
if (!state->sysconf_fields[i])
goto sysconf_error;
}
if (config->init && config->init(state))
goto sysconf_error;
if (config->pad_config &&
(state->pad_state = stm_pad_claim(config->pad_config,
dev_name(dev))) == NULL)
goto pad_error;
stm_device_power(state, stm_device_power_on);
return 0;
pad_error:
if (config->exit)
config->exit(state);
sysconf_error:
for (i--; i>=0; i--)
sysconf_release(state->sysconf_fields[i]);
return -EBUSY;
}
static void __stm_device_exit(struct stm_device_state *state)
{
struct stm_device_config *config = state->config;
int i;
stm_device_power(state, stm_device_power_off);
if (config->pad_config)
stm_pad_release(state->pad_state);
if (config->exit)
config->exit(state);
for (i = 0; i < config->sysconfs_num; i++)
sysconf_release(state->sysconf_fields[i]);
}
struct stm_device_state *stm_device_init(struct stm_device_config *config,
struct device *dev)
{
struct stm_device_state *state = kzalloc(sizeof(*state) +
sizeof(*state->sysconf_fields) * config->sysconfs_num,
GFP_KERNEL);
BUG_ON(!config);
BUG_ON(!dev);
if (state && __stm_device_init(state, config, dev) != 0)
state = NULL;
return state;
}
EXPORT_SYMBOL(stm_device_init);
void stm_device_exit(struct stm_device_state *state)
{
BUG_ON(!state);
__stm_device_exit(state);
kfree(state);
}
EXPORT_SYMBOL(stm_device_exit);
static void stm_device_devres_exit(struct device *dev, void *res)
{
struct stm_device_state *state = res;
BUG_ON(!state);
__stm_device_exit(state);
}
static int stm_device_devres_match(struct device *dev, void *res, void *data)
{
struct stm_device_state *state = res, *match = data;
return state == match;
}
struct stm_device_state *devm_stm_device_init(struct device *dev,
struct stm_device_config *config)
{
struct stm_device_state *state = devres_alloc(stm_device_devres_exit,
sizeof(*state) + sizeof(*state->sysconf_fields) *
config->sysconfs_num, GFP_KERNEL);
BUG_ON(!dev);
BUG_ON(!config);
if (state) {
if (__stm_device_init(state, config, dev) == 0) {
devres_add(dev, state);
} else {
devres_free(state);
state = NULL;
}
}
return state;
}
EXPORT_SYMBOL(devm_stm_device_init);
void devm_stm_device_exit(struct device *dev, struct stm_device_state *state)
{
int err;
__stm_device_exit(state);
err = devres_destroy(dev, stm_device_devres_exit,
stm_device_devres_match, state);
WARN_ON(err);
}
EXPORT_SYMBOL(devm_stm_device_exit);
static int stm_device_find_sysconf(struct stm_device_config *config,
const char *name)
{
int result = -1;
int i;
BUG_ON(!name);
for (i = 0; i < config->sysconfs_num; i++) {
struct stm_device_sysconf *pad_sysconf = &config->sysconfs[i];
if (strcmp(name, pad_sysconf->name) == 0) {
result = i;
break;
}
}
return result;
}
void stm_device_sysconf_write(struct stm_device_state *state,
const char* name, unsigned long long value)
{
int i;
i = stm_device_find_sysconf(state->config, name);
WARN_ON(i < 0);
if (i >= 0)
sysconf_write(state->sysconf_fields[i], value);
}
EXPORT_SYMBOL(stm_device_sysconf_write);
unsigned long long stm_device_sysconf_read(struct stm_device_state *state,
const char* name)
{
int i;
unsigned long long result = -1;
i = stm_device_find_sysconf(state->config, name);
WARN_ON(i < 0);
if (i >= 0)
result = sysconf_read(state->sysconf_fields[i]);
return result;
}
EXPORT_SYMBOL(stm_device_sysconf_read);
void stm_device_power(struct stm_device_state *device_state,
enum stm_device_power_state power_state)
{
if (device_state->power_state == power_state)
return;
if (device_state->config->power)
device_state->config->power(device_state, power_state);
device_state->power_state = power_state;
}
EXPORT_SYMBOL(stm_device_power);
struct stm_pad_state* stm_device_get_pad_state(struct stm_device_state *state)
{
return state->pad_state;
}
EXPORT_SYMBOL(stm_device_get_pad_state);
struct stm_device_config* stm_device_get_config(struct stm_device_state *state)
{
return state->config;
}
EXPORT_SYMBOL(stm_device_get_config);