From bb7ce467353c0891f6b010a31966f19db9183cbb Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Mon, 30 Dec 2013 23:07:53 +0000 Subject: [PATCH] more functionality, debugging --- drivers/elphel/elphel393-pwr.c | 1041 ++++++++++++++++++++++++++++++-- 1 file changed, 991 insertions(+), 50 deletions(-) diff --git a/drivers/elphel/elphel393-pwr.c b/drivers/elphel/elphel393-pwr.c index 8d13820..eab48c6 100644 --- a/drivers/elphel/elphel393-pwr.c +++ b/drivers/elphel/elphel393-pwr.c @@ -17,7 +17,7 @@ *! You should have received a copy of the GNU General Public License *! along with this program. If not, see . */ - +#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */ #include #include #include @@ -29,6 +29,7 @@ #include #include #include +#include #define DRIVER_DESCRIPTION "Elphel 10393 power supply control" #define DRIVER_VERSION "1.00" @@ -42,52 +43,272 @@ #define GPIO_CHIP2_ADDR 0x21 #define LTC3589_ADDR 0x34 +/* TODO: set resistors in device tree to accommodate different revisions ( elphel393_pwr,vp15_r1 = <357000>)*/ +#define VP15_R1 357000 +#define VP15_R2 287000 +#define VCC_SENS01_R1 787000 +#define VCC_SENS01_R2 287000 +#define VCC_SENS23_R1 787000 +#define VCC_SENS23_R2 287000 +#define VP5_R1 523000 +#define VP5_R2 100000 +#define VLDO18_R1 357000 +#define VLDO18_R2 287000 + +#define PINSTRAPPED_OVEN 1 +#define REF_FIXED_TENTH_MV 8000 +#define REF_VAR_0_TENTH_MV 3625 +#define REF_VAR_STEP_TENTH_MV 125 +#define DEAFULT_TIMEOUT 300 /* number of retries testing pgood before giving up */ +struct pwr_gpio_t { + const char * label; + int pin; + int dir; /* direction: 0 - in, 1 - out*/ + int out_val; /* output value */ +}; + struct elphel393_pwr_data_t { int chip_i2c_addr[3]; + struct device * ltc3489_dev; + struct pwr_gpio_t pwr_gpio [16]; int simulate; /* do not perform actual i2c writes */ struct mutex lock; + int pgoot_timeout; + int pinstrapped_oven; }; -struct pwr_gpio_t { - const char * label; - int pin; +struct voltage_reg_t { + const char * name; + int r1; /* resistor in ohms, if <=0 - r2 is voltage in mv */ + int r2; /* resistor in ohms, if r1<=0 - voltage in mv */ + int awe_ref; /* 0 - no control, -1 - margining VP10, -2 - margining VP18 */ + int awe_en; /* 0 - no control, negative - -1-gpio_index */ + int awe_pgood; /* 0 - no status , negative - -1-gpio_index */ + int mask_pgood; /* 1 - temporarily disable pgood when turning on/changing voltage */ + int awe_slew; }; +static struct voltage_reg_t voltage_reg[]={ + { + .name="vp15", + .r1=VP15_R1, + .r2=VP15_R2, + .awe_ref=LTC3589_AWE_B1DTV1_REF, + .awe_en=0, + .awe_pgood=LTC3589_AWE_PGSTAT_SD1, + .mask_pgood=1, + .awe_slew=LTC3589_AWE_VCCR_SLEW_SD1 + }, + { + .name="vcc_sens01", + .r1=VCC_SENS01_R1, + .r2=VCC_SENS01_R2, + .awe_ref=LTC3589_AWE_B2DTV2_REF, + .awe_en=LTC3589_AWE_OVEN_EN_SD2, + .awe_pgood=LTC3589_AWE_PGSTAT_SD2, + .mask_pgood=1, + .awe_slew=LTC3589_AWE_VCCR_SLEW_SD2 + }, + { + .name="vcc_sens23", + .r1=VCC_SENS23_R1, + .r2=VCC_SENS23_R2, + .awe_ref=LTC3589_AWE_B3DTV1_REF, + .awe_en=LTC3589_AWE_OVEN_EN_SD3, + .awe_pgood=LTC3589_AWE_PGSTAT_SD3, + .mask_pgood=1, + .awe_slew=LTC3589_AWE_VCCR_SLEW_SD3 + }, + { + .name="vp5", + .r1=VP5_R1, + .r2=VP5_R2, + .awe_ref=0, + .awe_en=LTC3589_AWE_OVEN_EN_BB, + .awe_pgood=LTC3589_AWE_PGSTAT_BB, + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="vldo18", + .r1=VLDO18_R1, + .r2=VLDO18_R2, + .awe_ref=0, + .awe_en= 0, + .awe_pgood=LTC3589_AWE_PGSTAT_LDO1, + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="vp33sens01", + .r1=-1, + .r2=33000, + .awe_ref=0, + .awe_en= -7, /* SENSPWREN0 */ + .awe_pgood=0, + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="vp33sens23", + .r1=-1, + .r2=33000, + .awe_ref=0, + .awe_en= -8, /* SENSPWREN1 */ + .awe_pgood=0, + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="mmtavcc10", + .r1=-1, + .r2=10000, + .awe_ref=0, + .awe_en= 0, + .awe_pgood=-15, /* MGTAVTTGOOD */ + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="mmtavtt12", + .r1=-1, + .r2=12000, + .awe_ref=0, + .awe_en= 0, + .awe_pgood=-15, /* MGTAVTTGOOD */ + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="vp10", + .r1=-1, + .r2=10000, + .awe_ref=-1, + .awe_en= 0, + .awe_pgood=-16, /* PGOOD18 */ + .mask_pgood=1, + .awe_slew=0 + }, + { + .name="vp18", + .r1=-1, + .r2=18000, + .awe_ref=-2, + .awe_en= 0, + .awe_pgood=-16, /* PGOOD18 */ + .mask_pgood=1, + .awe_slew=0 + }, +}; -static struct pwr_gpio_t pwr_gpio [16]={ +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) */ + {"PWR_MGB1", 0, 0, 0}, /* 1.8V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */ + {"PWR_MG1", 1, 0, 0}, /* 1.8V margining enable 0 - negative margining, 1 - positive margining, float - no margining */ + {"PWR_MGB0", 2, 0, 0}, /* 1.0V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */ + {"PWR_MG0", 3, 0, 0}, /* 1.0V margining enable 0 - negative margining, 1 - positive margining, float - no margining */ + {"PWR_FQ0", 4, 0, 0}, /* float - nominal frequency (should float for SS), 0 - 0.67 nominal frequency, 1 - 1.5 nominal frequency */ + {"PWR_SS", 5, 0, 0}, /* Spread spectrum, 0 or float - spread spectrum disabled */ + {"SENSPWREN0", 6, 0, 0}, /* 1 - enable 3.3 power to sensor connectors J6 and J7 (0 or float - disable) */ + {"SENSPWREN1", 7, 0, 0}, /* 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 */ + {"NSHUTDOWN", 8, 0, 0}, /* (pulled up). 0 - shutdown, 1 normal */ + {"DIS_POR", 9, 0, 0}, /* (pulled down). 0 - normal, 1 - disable POR generation on PGOOD deassertion (needed whil changing voltages) */ + { NULL, 10, 0, 0}, /* Not connected */ + { NULL, 11, 0, 0}, /* Not connected */ + { NULL, 12, 0, 0}, /* Not connected */ + { NULL, 13, 0, 0}, /* Not connected */ + {"MGTAVTTGOOD",14, 0, 0}, /* (input) 1.2V linear regulator status (generated from 1.8V) */ + {"PGOOD18", 15, 0, 0} /* (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 int make_group (struct device *dev, const char * name, + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count)); +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); +static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf); + +#if 0 +static ssize_t output_state_show(struct device *dev, struct device_attribute *attr, char *buf); +#endif +static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t pgood_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t pbad_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t enable_por_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + +static int por_ctrl(struct device *dev, int disable_por); +static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por); +static int reenable_por(struct device *dev); +static int wait_all_pgood(struct device *dev); +static int list_chn_bits(char * buf, int chn_bits); +static int parse_chn_bits(const char * buf); +static int get_enabled_mask(struct device *dev); +static int set_enabled_by_mask(struct device *dev, int chn_bits, int enable); +static int slew_by_mask(struct device *dev, int chn_bits); +static int get_voltage_channel(const char * name); +static int get_gpio_index_by_name(const char * name); +static int gpio_conf_by_index(struct device *dev,int gpio_index, int dir, int val); +static int get_gpio_pwr_mgx_indices(int chn, int * indices); /* chn = 0 (VP10) or 1 (VP18) */ +static int get_volt_mv(struct device *dev, int chn); +static int set_volt_mv(struct device *dev, int chn, int v_mv); +static int get_enable(struct device *dev, int chn); +static int set_enable(struct device *dev, int chn, int enable); +static int get_pgood(struct device *dev, int chn); +/* + Voltages: + VP10 (on at power up, nominal 1.0V) + VP18 (on at power up, nomianl 1.8V) + VP15 (SW1, on by pinstrap, nominal 1.5V - may be reduced to 1.35 later) + VCC_SENS01 (SW2, nominal 1.8V, max 2.8V) + VCC_SENS23 (SW3, nominal 1.8V, max 2.8V) + VP5 (nominal 5.0V, not software programmed) + VLDO18 (LDO1 - always on) + VP33SENS0 - 3.3V to sensors J6,J7 + VP33SESN1 - 3.3V to sensors J8,J9 + MGTAVCC10 - 1.0 V, linear from VP18 (pgood controls MGTAVTT12) + MGTAVTT12 - 1.2 V, linear from VP18 (pgood available, means both) + LTC3589 used channels : LDO1, SW1, SW2, SW3, BB + + TODO: Change VCC_SENS01_R1, VCC_SENS23_R1 to 787K (now 487) + */ + /* 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 DEVICE_ATTR(output_state, SYSFS_PERMISSIONS & SYSFS_READONLY, outputs_all_show, NULL); +static DEVICE_ATTR(configs, SYSFS_PERMISSIONS & SYSFS_READONLY, configs_all_show, NULL); + +static DEVICE_ATTR(channels_en, SYSFS_PERMISSIONS, channels_en_show, channels_en_store); +static DEVICE_ATTR(channels_dis,SYSFS_PERMISSIONS, channels_dis_show, channels_dis_store); +static DEVICE_ATTR(power_good, SYSFS_PERMISSIONS & SYSFS_READONLY, pgood_show, NULL); +static DEVICE_ATTR(power_bad, SYSFS_PERMISSIONS & SYSFS_READONLY, pbad_show, NULL); +static DEVICE_ATTR(enable_por, SYSFS_PERMISSIONS, enable_por_show, enable_por_store); static struct attribute *root_dev_attrs[] = { &dev_attr_simulate.attr, + &dev_attr_output_state.attr, + &dev_attr_configs.attr, + &dev_attr_channels_en.attr, + &dev_attr_channels_dis.attr, + &dev_attr_power_good.attr, + &dev_attr_power_bad.attr, + &dev_attr_enable_por.attr, NULL }; static const struct attribute_group dev_attr_root_group = { @@ -95,7 +316,46 @@ static const struct attribute_group dev_attr_root_group = { .name = NULL, }; -static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf) +static int make_group (struct device *dev, const char * name, + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count)) +{ + int retval=-1; + int index; + struct attribute **pattrs; /* array of pointers to attibutes */ + struct device_attribute *dev_attrs; + struct attribute_group *attr_group; + pattrs = devm_kzalloc(dev,(ARRAY_SIZE(voltage_reg)+1)*sizeof(pattrs[0]), GFP_KERNEL); + if (!pattrs) return -ENOMEM; + dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0]), GFP_KERNEL); + if (!dev_attrs) return -ENOMEM; + attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL); + if (!attr_group) return -ENOMEM; + memset(dev_attrs, 0, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0])); + memset(attr_group, 0, sizeof(*attr_group)); + for (index=0;indexname = name; + attr_group->attrs =pattrs; + dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj)); + if (&dev->kobj) { + retval = sysfs_create_group(&dev->kobj, attr_group); + } + return retval; +} + + +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); @@ -103,45 +363,721 @@ static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, 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)); + struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev); sscanf(buf, "%du", &clientdata->simulate); + ltc3589_set_simulate(ltc3589_client, clientdata->simulate); + return count; +} + +static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn, pg; + char * cp = buf; + for (chn=0;chn0)?get_pgood(dev, chn):-1; + buf+=sprintf(buf,"%s: %s %d mV%s\n", + voltage_reg[chn].name, + get_enable(dev, chn)?"ON":"OFF", + get_volt_mv(dev, chn), + (pg==1)?", power good":((pg==0)?", power is NOT good":"") + ); + } + return buf-cp; +} +static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn, pg; + char * cp = buf; + for (chn=0;chnattr.name); + if (chn<0) return chn; + pg=get_pgood(dev, chn); + return sprintf(buf,"%s: %s %d mV, %s\n", + voltage_reg[chn].name, + get_enable(dev, chn)?"ON":"OFF", + get_volt_mv(dev, chn), + (pg=1)?"power good":((pg==0)?"power is NOT good":"") + ); +} +#endif +static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn; + chn=get_voltage_channel(attr->attr.name); + if (chn<0) return chn; + return sprintf(buf,"%d\n", get_enable(dev, chn)); +} + +static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn, enable; + chn=get_voltage_channel(attr->attr.name); + if (chn<0) return chn; + sscanf(buf, "%du", &enable); return count; } +static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn; + chn=get_voltage_channel(attr->attr.name); + if (chn<0) return chn; + return sprintf(buf,"%d\n", get_pgood(dev, chn)); +} + +static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn_bits; + char * cp=buf; + chn_bits=get_enabled_mask(dev); + if (chn_bits<0) return chn_bits; + buf+=list_chn_bits(buf, chn_bits); + buf+=sprintf(buf,"\n"); + return buf-cp; +} +/* also slews DAC(s) if applilcable. Call after changing voltage on enabled channels */ +static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn_bits,rc,old_dis_por,pre_disabled; + chn_bits=parse_chn_bits(buf); + pre_disabled=get_and_disable_por(dev, chn_bits, &old_dis_por); + if (pre_disabled<0) return pre_disabled; + rc=slew_by_mask(dev, chn_bits); /* slew if needed - before enabling, waits for slew over */ + if (rc<0) return rc; + rc=set_enabled_by_mask(dev, chn_bits, 1); + if (rc<0) return rc; + if (pre_disabled && (old_dis_por==0)){ + rc=reenable_por(dev); /* will wait pgood */ + if (rc<0) return rc; + } + return count; +} + +static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn_bits; + char * cp=buf; + chn_bits=get_enabled_mask(dev); + if (chn_bits<0) return chn_bits; + chn_bits=~chn_bits; + buf+=list_chn_bits(buf, chn_bits); + buf+=sprintf(buf,"\n"); + return buf-cp; +} + +static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn_bits,rc; + chn_bits=parse_chn_bits(buf); + rc=set_enabled_by_mask(dev, chn_bits, 0); + if (rc<0) return rc; + return count; +} + +static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int chn; + chn=get_voltage_channel(attr->attr.name); + if (chn<0) return chn; + return sprintf(buf,"%d\n",get_volt_mv(dev, chn)); +} + +static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int chn, v_mv; + int rc,old_dis_por,pre_disabled; + chn=get_voltage_channel(attr->attr.name); + if (chn<0) return chn; + /* if output was enabled, and pgood negation may cause POR, disable POR (later restore) */ + if (get_enable(dev,chn)) pre_disabled=get_and_disable_por(dev, 1<0) pgood_bits |= (1<pwr_gpio[gpio_disable_por_index].out_val)?0:1); +} + +/* When enable_por is set to 1, it first waits for PGOOD and does not enable POR on error */ +static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int en_por,rc; + sscanf(buf, "%du", &en_por); + if (en_por) rc=reenable_por(dev); /* will wait pgood, then enable POR */ + else rc=por_ctrl(dev, 1); /* disable POR */ + if (rc<0) return rc; + return count; +} + +int por_ctrl(struct device *dev, int disable_por) +{ + int gpio_disable_por_index=get_gpio_index_by_name("DIS_POR"); + if (gpio_disable_por_index<0) return gpio_disable_por_index; + return gpio_conf_by_index(dev, gpio_disable_por_index, 1, disable_por); +} +/* + * disable POR (if needed) before changing value or enabling one of the voltages + * chn_bits - 1 bit per channel + */ + +static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por) +{ + int rc,chn; + int gpio_disable_por_index; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + gpio_disable_por_index=get_gpio_index_by_name("DIS_POR"); + if (gpio_disable_por_index<0) return gpio_disable_por_index; + old_dis_por[0]=clientdata->pwr_gpio[gpio_disable_por_index].out_val; + for (chn=0;chn=ARRAY_SIZE(voltage_reg)) return 0; /* POR was not required to be disabled */ + rc = gpio_conf_by_index(dev, gpio_disable_por_index, 1, 1); /* out turn on "disable_por" */ + if (rc<0) return rc; + return 1; /* pgood-based POR was disabled (could already be disabled)*/ +} + +/* call if POR was diasabled before changing voltage (value or enabling), after waiting for pgood*/ +static int reenable_por(struct device *dev) +{ + int gpio_disable_por_index, rc; + gpio_disable_por_index=get_gpio_index_by_name("DIS_POR"); + if (gpio_disable_por_index<0) return gpio_disable_por_index; + if (((rc=wait_all_pgood(dev)))<0) return rc; + return gpio_conf_by_index(dev, gpio_disable_por_index, 1, 0); /* out turn off "disable_por" */ +} + +static int wait_all_pgood(struct device *dev) +{ + int ntry,chn,all_good=0; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + + for (ntry=0;ntrypgoot_timeout;ntry++){ + all_good=1; + for (chn=0;chn0) && (get_pgood(dev,chn)!=1)){ /* enabled or always enabled */ + all_good=0; + break; + } + } + if (all_good) break; /* all enabled channels that have pgood control are good */ + } + if (!all_good) return -EAGAIN; + return 0; +} + +static int list_chn_bits(char * buf, int chn_bits) +{ + int chn; + char * cp=buf; + for (chn=0;chn0) en_mask|= (1<0){ + awe |= voltage_reg[chn].awe_en; + } + } + awe &= 0xff; /* just WE mask */ + if (awe){ + dev_dbg(dev,"set_enabled_by_mask(), cumulative awe=0x%x\n",awe); + ltc3589_client = to_i2c_client(clientdata->ltc3489_dev); + oven=ltc3589_read_field (ltc3589_client, LTC3589_AWE_OVEN); + if (oven<0) return oven; + if (enable) oven |= awe; + else oven &= ~awe; + return ltc3589_write_field (ltc3589_client, oven, LTC3589_AWE_OVEN); + } + return 0; +} + +static int slew_by_mask(struct device *dev, int chn_bits) +{ + /* assuming all slew bits in LTC3589 to be in a single register (LTC3589_AWE_OVEN) */ + int chn, slew=0,rc,ntry; + u32 adwe; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + struct i2c_client *ltc3589_client; + ltc3589_client = to_i2c_client(clientdata->ltc3489_dev); + dev_dbg(dev,"slew_by_mask(dev,0x%x)\n",chn_bits); + for (chn=0;chn%d (slew = 0x%x)\n",adwe,rc,slew); + if (rc<0) return rc; + /* wait slew over */ + for (ntry=0;ntrypgoot_timeout;ntry++){ + rc=ltc3589_read_field(ltc3589_client, LTC3589_AWE_VCCR); + dev_dbg(dev,"slew_by_mask():ltc3589_read_field(ltc3589_client, 0x%x)->0x%x(%d)\n",LTC3589_AWE_VCCR,rc,rc); + if (rc<0) return rc; + if ((rc & slew) ==0 ) break; + } + if (ntry>=clientdata->pgoot_timeout) return -EAGAIN; + } + return 0; +} + +/* name should either completely match, or have "_*" suffix */ +static int get_voltage_channel(const char * name) +{ + int i; + for (i=0;i=ARRAY_SIZE(clientdata->pwr_gpio))) return -EINVAL; + if ((clientdata->pwr_gpio[gpio_index].dir==dir) && ((clientdata->pwr_gpio[gpio_index].out_val==val) || (dir==0))){ + dev_dbg(dev,"GPIO#%d(index=%d) did not change: old dir=%d, new dir=%d, old val = %d, new val=%d\n", + clientdata->pwr_gpio[gpio_index].pin, + gpio_index, + clientdata->pwr_gpio[gpio_index].dir, + dir, + clientdata->pwr_gpio[gpio_index].out_val, + val); + return 0; + } + clientdata->pwr_gpio[gpio_index].dir=dir?1:0; + clientdata->pwr_gpio[gpio_index].out_val=val?1:0; + if (clientdata->pwr_gpio[gpio_index].dir){ + if (!clientdata->simulate) rc=gpio_direction_output(clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val); + dev_dbg(dev,"gpio_direction_output(%d,%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val,rc); + } else { + if (!clientdata->simulate) rc=gpio_direction_input(clientdata->pwr_gpio[gpio_index].pin); + dev_dbg(dev,"gpio_direction_input(%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin,rc); + } + return rc; +} +static int get_gpio_pwr_mgx_indices(int chn, int * indices) /* chn = 0 (VP10) or 1 (VP18) */ +{ + indices[0]=get_gpio_index_by_name(chn?"PWR_MG1": "PWR_MG0"); + indices[1]=get_gpio_index_by_name(chn?"PWR_MGB1":"PWR_MGB0"); + return ((indices[0]>=0) && (indices[1]>=0))?0:-EINVAL; +} + +/* calculate output voltage in mV */ +static int get_volt_mv(struct device *dev, int chn) +{ + int v_mv,ref,rc; + int pwr_mg_indices[2]; + s64 num; + struct i2c_client *ltc3589_client; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL; + if (voltage_reg[chn].r1<=0) { + if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/ + rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */ + if (rc<0) return rc; + if (clientdata->pwr_gpio[pwr_mg_indices[0]].dir==0) ref=0; + else if (clientdata->pwr_gpio[pwr_mg_indices[0]].out_val) ref=1; + else ref=-1; + if (ref) { + if (clientdata->pwr_gpio[pwr_mg_indices[1]].dir==0) ref*=15; + else if (clientdata->pwr_gpio[pwr_mg_indices[1]].out_val) ref*=10; + else ref*= 5; + } + v_mv=(voltage_reg[chn].r2*(100+ref)*2+10)/2000; + } else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */ + v_mv=(voltage_reg[chn].r2+5)/10; + } + + } else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */ +#if 0 + v_mv=(REF_FIXED_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2)/(10*voltage_reg[chn].r2); +#endif + num=((u64) REF_FIXED_TENTH_MV)* (voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2; + v_mv=(int) div64_u64(num, 10*voltage_reg[chn].r2); + dev_dbg(dev,"chn=%d REF_FIXED_TENTH_MV=%d .r1=%d .r2=%d v_mv=%d\n",chn, REF_FIXED_TENTH_MV,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv); + } else { /* vp15, vcc_sens01,vcc_sens23 */ + ltc3589_client = to_i2c_client(clientdata->ltc3489_dev); + ref=ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_ref); + if (ref<0) return ref; + num=(REF_VAR_0_TENTH_MV+ REF_VAR_STEP_TENTH_MV* ref); + num=num*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2; + v_mv=div64_u64(num, 10*voltage_reg[chn].r2); + dev_dbg(dev,"chn=%d ref=%d .r1=%d .r2=%d v_mv=%d\n",chn, ref,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv); + } + return v_mv; +} +/* 0 - OK, <0 - error */ +/* does not iclude disabling/re-enabling PoR */ +static int set_volt_mv(struct device *dev, int chn, int v_mv) +{ + int rc,index,d; + s64 num; + int pwr_mg_indices[2]; + struct i2c_client *ltc3589_client; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL; + dev_dbg(dev,"set_volt_mv(dev,%d,%d),.r1=%d\n",chn,v_mv,voltage_reg[chn].r1); + if (voltage_reg[chn].r1<=0) { + if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/ + index=(400*v_mv+voltage_reg[chn].r2)/(2*voltage_reg[chn].r2); + dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2); + if ((index<17) || (index>23)) { + dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name, + (17*voltage_reg[chn].r2)/200,(23*voltage_reg[chn].r2)/200); + return -EINVAL; + } + /* disable -> chnage -> enable (if needed) */ + rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */ + if (rc<0) return rc; + rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 0, 0); /* disable margining */ + if (rc < 0)return rc; + if (index !=20){ + /* set margining absolute value */ + switch (index) { + case 17: + case 23: + rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 0, 0); /* float: +/- 15% */ + break; + case 18: + case 22: + rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 1); /* out 1: +/- 10% */ + break; + case 19: + case 21: + rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 0); /* out 0: +/- 5% */ + break; + } + if (rc < 0)return rc; + /* set margining sign */ + if (index >20) rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 1); /* out 1: positive margining */ + else rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 0); /* out 0: negative margining */ + if (rc < 0)return rc; + } + } else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */ + return -EINVAL; /* voltage not regulated */ + } + } else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */ + return -EINVAL; /* voltage not regulated */ + } else { /* vp15, vcc_sens01,vcc_sens23 */ + ltc3589_client = to_i2c_client(clientdata->ltc3489_dev); +#if 0 + index=((10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2)*(voltage_reg[chn].r1+voltage_reg[chn].r2))/ + ((voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV); + num=(10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2); + num*=(voltage_reg[chn].r1+voltage_reg[chn].r2); + index=div64_u64(num, (voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV); +#endif + num= (10LL*v_mv*voltage_reg[chn].r2) - ((s64) (voltage_reg[chn].r1+voltage_reg[chn].r2))*REF_VAR_0_TENTH_MV; + d= REF_VAR_STEP_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2); + index=div64_u64(num +(d>>1), d); + dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2); + dev_dbg(dev,"index=%d\n",index); + if ((index<0) || (index>31)){ + dev_err(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2); + dev_err(dev,"REF_VAR_0_TENTH_MV=%d REF_VAR_STEP_TENTH_MV=%d\n",REF_VAR_0_TENTH_MV,REF_VAR_STEP_TENTH_MV); + dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name, + (int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV* 0))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2), + 10*voltage_reg[chn].r2), + (int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV*31))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2), + 10*voltage_reg[chn].r2)); + return -EINVAL; + } + dev_dbg(dev,"ltc3589_client->name= %s\n", ltc3589_client->name); + rc=ltc3589_write_field(ltc3589_client, index,voltage_reg[chn].awe_ref); + if (rc<0) return rc; + } + return 0; +} + +/* get output enable state */ +static int get_enable(struct device *dev, int chn) +{ + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev); + if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL; + if (voltage_reg[chn].awe_en==0) { + return 2; /* always on */ + } else if (voltage_reg[chn].awe_en>0){ + if (clientdata->pinstrapped_oven & voltage_reg[chn].awe_en) return 1; /* pin-strapped on bit */ + return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_en); + } else { + return (clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].dir && clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].out_val)?1:0; + } +} + +/* set output enable state */ +static int set_enable(struct device *dev, int chn, int enable) +{ + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev); + if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL; + if (voltage_reg[chn].awe_en==0) { + return -EINVAL; /* always on, not controlled */ + } else if (voltage_reg[chn].awe_en>0){ + return ltc3589_write_field(ltc3589_client, enable, voltage_reg[chn].awe_en); + } else { + return gpio_conf_by_index(dev,-1-voltage_reg[chn].awe_en, 1, enable); + } +} + +/* get power good state */ +static int get_pgood(struct device *dev, int chn) +{ + int rc; + struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev)); + struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev); + if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL; + if (voltage_reg[chn].awe_pgood==0) { + if (((rc=get_enable(dev,chn)))<0) return rc; /* 0 - disabled */ + return 2; /* no status available */ + } else if (voltage_reg[chn].awe_pgood>0){ + return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_pgood); + } else { +/* return gpio_get_value(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin); */ + return gpio_get_value_cansleep(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin); + } +} + 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; + if (((retval = make_group (dev, "voltages_mv", output_ref_show, output_ref_store)))<0) return retval; + if (((retval = make_group (dev, "outputs_en", output_en_output_show, output_en_output_store)))<0) return retval; + if (((retval = make_group (dev, "outputs_pgood", outputs_pgood_show, NULL)))<0) return retval; } return retval; } -static void elphel393_pwr_init_of(struct platform_device *pdev) + +static void elphel393_pwr_init_of_i2caddr(struct platform_device *pdev) { const __be32 * config_data; -// struct device *dev = &pdev->dev; + int len,i; 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); + dev_dbg(&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;ichip_i2c_addr[i]=be32_to_cpup(&config_data[i]); } + } +} + +static void elphel393_pwr_init_of(struct platform_device *pdev) +{ + const __be32 * config_data; + const char * config_string; + char str[40]; + int len,chn,pre_disabled,old_dis_por,rc,chn_bits; + struct device_node *node = pdev->dev.of_node; + struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev); + struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev); + + if (node) { + /* find resistor values */ + for (chn=0;chn0)){ + dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0])); + voltage_reg[chn].r1=be32_to_cpup(&config_data[0]); + } + sprintf(str,"elphel393_pwr,%s.r2",voltage_reg[chn].name); + config_data = of_get_property(node, str, &len); + if (config_data && (len>0)){ + dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0])); + voltage_reg[chn].r2=be32_to_cpup(&config_data[0]); + } + } + /* which channels are enabled by pin-strapping */ + config_data = of_get_property(node, "elphel393_pwr,pinstrapped_oven", &len); + if (config_data && (len>0)){ + dev_dbg(&pdev->dev,"Found elphel393_pwr,pinstrapped_oven=<%d>\n",be32_to_cpup(&config_data[0])); + clientdata->pinstrapped_oven=be32_to_cpup(&config_data[0]); + } + + /* debug mode - simulate only, no actual power supply control */ + config_data = of_get_property(node, "elphel393_pwr,simulate", &len); + if (config_data && (len>0)){ + dev_dbg(&pdev->dev,"Found elphel393_pwr,simulate=<%d>\n",be32_to_cpup(&config_data[0])); + clientdata->simulate=config_data[0]?1:0; + ltc3589_set_simulate(ltc3589_client, clientdata->simulate); + } + + /* disable output voltages (not likely to be needed - maybe for warm reboot) */ + config_string = of_get_property(node, "elphel393_pwr,channels_disable", &len); + if (config_string){ + dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_disable=\"%s\"\n",config_string); + chn_bits=parse_chn_bits(config_string); + rc=set_enabled_by_mask(&pdev->dev, chn_bits, 0); + if (rc<0) return; + } + /* set output voltages (target voltages, in mV) */ + for (chn=0;chn0)){ + dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0])); + if (get_enable(&pdev->dev,chn)) pre_disabled=get_and_disable_por(&pdev->dev, 1<dev,"pre_disabled=%d\n",pre_disabled); + rc=set_volt_mv(&pdev->dev, chn,be32_to_cpup(&config_data[0])); + dev_dbg(&pdev->dev,"set_volt_mv()->%d\n",rc); + if (rc<0) return; + if (pre_disabled && (old_dis_por==0)){ + rc=reenable_por(&pdev->dev); /* will wait pgood */ + if (rc<0){ + dev_err(&pdev->dev,"Timeout during wait for power good after chnging voltage for %s before re-enabling POR on power loss\n",\ + voltage_reg[chn].name); + return; + } + } + } + } + /* enable output voltages */ + config_string = of_get_property(node, "elphel393_pwr,channels_enable", &len); + if (config_string){ + dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_enable=\"%s\"\n",config_string); + chn_bits=parse_chn_bits(config_string); + pre_disabled=get_and_disable_por(&pdev->dev, chn_bits, &old_dis_por); + if (pre_disabled<0) return; + rc=slew_by_mask(&pdev->dev, chn_bits); /* slew if needed - before enabling, waits for slew over */ + if (rc<0) { + dev_err(&pdev->dev,"Timeout during wait for slew over\n"); + return; + } + rc=set_enabled_by_mask(&pdev->dev, chn_bits, 1); + if (rc<0) return; + if (pre_disabled && (old_dis_por==0)){ + rc=reenable_por(&pdev->dev); /* will wait pgood */ + if (rc<0) { + dev_err(&pdev->dev,"Timeout during wait for power good before re-enabling POR on power loss\n"); + return; + } + } + } } + dev_info(&pdev->dev,"elphel393_pwr configuration done\n"); } 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); + dev_dbg(dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr); return i2c_verify_client(dev) && (client->addr==addr[0]); } @@ -154,69 +1090,74 @@ 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); + dev_dbg(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; +// 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 +++"); + dev_info(&pdev->dev,"Probing elphel393-pwr\n"); clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL); + clientdata->pgoot_timeout=DEAFULT_TIMEOUT; + clientdata->pinstrapped_oven=PINSTRAPPED_OVEN; + 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); - +// elphel393_pwr_init_of(pdev); + elphel393_pwr_init_of_i2caddr(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]); + dev_dbg(&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>3]+(i & 7); - rc=gpio_request(pwr_gpio[i].pin, pwr_gpio[i].label); + clientdata->pwr_gpio[i].label=pwr_gpio[i].label; + clientdata->pwr_gpio[i].pin=base[i>>3]+(i & 7); + clientdata->pwr_gpio[i].dir=0; /* input */ + clientdata->pwr_gpio[i].out_val=0; + rc=gpio_request(clientdata->pwr_gpio[i].pin, clientdata->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); + dev_err(&pdev->dev," Failed to get GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label); + return rc; } else { - dev_info(&pdev->dev,"Confirmed request GPIO[%d] with label %s\n",pwr_gpio[i].pin,pwr_gpio[i].label); + dev_dbg(&pdev->dev,"Confirmed request GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label); } } /* find ltc3589 */ - ltc3489_dev=find_device_by_i2c_addr(LTC3589_ADDR); - if (!ltc3489_dev){ + clientdata->ltc3489_dev=find_device_by_i2c_addr(LTC3589_ADDR); + if (!clientdata->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)); + ltc3589_client = to_i2c_client(clientdata->ltc3489_dev); + + dev_dbg(&pdev->dev,"Located %s with i2c address 0x%02x\n",ltc3589_client->name,LTC3589_ADDR); + dev_dbg(&pdev->dev,"LTC3589 status= 0x%02x\n",ltc3589_read_field(ltc3589_client, LTC3589_AWE_PGSTAT)); + elphel393_pwr_init_of(pdev); return 0; } - static int elphel393_pwr_remove(struct platform_device *pdev) { - dev_info(&pdev->dev,"+++ Removing elphel393-pwr +++"); + 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 */} -- 2.18.1