Commit da77e5ab authored by Andrey Filippov's avatar Andrey Filippov

continue work on elphel393-pwr.c

parent a56a0878
......@@ -22,10 +22,219 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/i2c/ltc3589.h>
#define DRIVER_DESCRIPTION "Elphel 10393 power supply control"
#define DRIVER_VERSION "1.00"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define GPIO_CHIP1_ADDR 0x20
#define GPIO_CHIP2_ADDR 0x21
#define LTC3589_ADDR 0x34
struct elphel393_pwr_data_t {
int chip_i2c_addr[3];
int simulate; /* do not perform actual i2c writes */
struct mutex lock;
};
struct pwr_gpio_t {
const char * label;
int pin;
};
static struct pwr_gpio_t pwr_gpio [16]={
/* 0x20: */
{"PWR_MGB1", 0}, /* 1.8V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG1", 1}, /* 1.8V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_MGB0", 2}, /* 1.0V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG0", 3}, /* 1.0V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_FQ0", 4}, /* float - nominal frequency (should float for SS), 0 - 0.67 nominal frequency, 1 - 1.5 nominal frequency */
{"PWR_SS", 5}, /* Spread spectrum, 0 or float - spread spectrum disabled */
{"SENSPWREN0", 6}, /* 1 - enable 3.3 power to sensor connectors J6 and J7 (0 or float - disable) */
{"SENSPWREN1", 7}, /* 1 - enable 3.3 power to sensor connectors J8 and J9 (0 or float - disable) */
/* 0x21: */
{"NSHUTDOWN", 8}, /* (pulled up). 0 - shutdown, 1 normal */
{"DIS_POR", 9}, /* (pulled down). 0 - normal, 1 - disable POR generation on PGOOD deassertion (needed whil changing voltages) */
{ NULL, 10}, /* Not connected */
{ NULL, 11}, /* Not connected */
{ NULL, 12}, /* Not connected */
{ NULL, 13}, /* Not connected */
{"MGTAVTTGOOD",14}, /* (input) 1.2V linear regulator status (generated from 1.8V) */
{"PGOOD18", 15} /* (input). Combines other voltages, can be monitored when DIS_POR is activated */
};
static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
/* root directory */
//static DEVICE_ATTR(outputs, SYSFS_PERMISSIONS & SYSFS_READONLY, output_description_show, NULL);
//static DEVICE_ATTR(status, SYSFS_PERMISSIONS & SYSFS_READONLY, status_show, NULL);
static DEVICE_ATTR(simulate, SYSFS_PERMISSIONS, simulate_show, simulate_store);
static struct attribute *root_dev_attrs[] = {
&dev_attr_simulate.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
return sprintf(buf, "%d\n",clientdata->simulate);
}
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
sscanf(buf, "%du", &clientdata->simulate);
return count;
}
static int elphel393_pwr_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
static void elphel393_pwr_init_of(struct platform_device *pdev)
{
const __be32 * config_data;
// struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev);
int len,i;
if (node) {
config_data = of_get_property(node, "elphel393_pwr,i2c_chips", &len);
if (config_data){
len /= sizeof(*config_data);
dev_info(&pdev->dev,"Found %d items in 'elphel393_pwr,i2c_chips' in the Device Tree\n",len);
if (len!= ARRAY_SIZE(clientdata->chip_i2c_addr)){
dev_err(&pdev->dev,"Got %d items in 'elphel393_pwr,i2c_chips', expected %d\n",len,ARRAY_SIZE(clientdata->chip_i2c_addr));
return;
}
for (i=0;i<len;i++) clientdata->chip_i2c_addr[i]=be32_to_cpup(&config_data[i]);
}
}
}
static int device_by_i2c_addr_match(struct device *dev, void *data)
{
struct i2c_client *client = to_i2c_client(dev);
int *addr = (int *)data;
dev_info(dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(dev) && (client->addr==addr[0]);
}
static struct device * find_device_by_i2c_addr(int address)
{
return bus_find_device(&i2c_bus_type, NULL, &address, device_by_i2c_addr_match);
}
static int i2c_addr_gpiochip_match(struct gpio_chip *chip, void *data)
{
struct i2c_client *client = to_i2c_client(chip->dev);
int *addr = (int *)data;
dev_info(chip->dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(chip->dev) && (client->addr==addr[0]);
}
static int elphel393_pwr_probe(struct platform_device *pdev)
{
struct gpio_chip *chip;
struct device * ltc3489_dev;
int i,rc;
int base[2];
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata = NULL;
dev_info(&pdev->dev,"+++ Probing elphel393-pwr +++");
clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL);
clientdata->chip_i2c_addr[0]=0x20;
clientdata->chip_i2c_addr[1]=0x21;
clientdata->chip_i2c_addr[2]=0x34;
platform_set_drvdata(pdev, clientdata);
elphel393_pwr_sysfs_register(pdev);
elphel393_pwr_init_of(pdev);
mutex_init(&clientdata->lock);
/* locate GPIO chips by i2c address */
for (i=0;i<2;i++){
chip = gpiochip_find(&clientdata->chip_i2c_addr[i], i2c_addr_gpiochip_match);
base[i]=chip->base;
dev_info(&pdev->dev,"Found gpio_chip with i2c_addr=0x%02x, label=%s, base=0x%x\n",clientdata->chip_i2c_addr[i],chip->label,base[i]);
}
for (i=0;i<ARRAY_SIZE(pwr_gpio);i++) if (pwr_gpio[i].label){
pwr_gpio[i].pin=base[i>>3]+(i & 7);
rc=gpio_request(pwr_gpio[i].pin, pwr_gpio[i].label);
if (rc<0){
dev_err(&pdev->dev," Failed to get GPIO[%d] with label %s\n",pwr_gpio[i].pin,pwr_gpio[i].label);
} else {
dev_info(&pdev->dev,"Confirmed request GPIO[%d] with label %s\n",pwr_gpio[i].pin,pwr_gpio[i].label);
}
}
/* find ltc3589 */
ltc3489_dev=find_device_by_i2c_addr(LTC3589_ADDR);
if (!ltc3489_dev){
dev_err(&pdev->dev," Failed to find LTC3489 with i2c address 0x%02x\n",LTC3589_ADDR);
return -EIO;
}
ltc3589_client = to_i2c_client(ltc3489_dev);
dev_info(&pdev->dev,"Located %s with i2c address 0x%02x\n",ltc3589_client->name,LTC3589_ADDR);
dev_info(&pdev->dev,"LTC3589 status= 0x%02x\n",read_field_ltc3589(ltc3589_client, LTC3589_AWE_PGSTAT));
return 0;
}
static int elphel393_pwr_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev,"+++ Removing elphel393-pwr +++");
return 0;
}
static struct of_device_id elphel393_pwr_of_match[] = {
{ .compatible = "elphel,elphel393-pwr-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_pwr_of_match);
static struct platform_driver elphel393_pwr = {
.probe = elphel393_pwr_probe,
.remove = elphel393_pwr_remove,
.driver = {
.name = "elphel393-pwr",
.owner = THIS_MODULE,
.of_match_table = elphel393_pwr_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_pwr);
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>");
MODULE_DESCRIPTION("Elphel 10393 power supply control");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment