diff --git a/drivers/misc/ltc3589.c b/drivers/misc/ltc3589.c
new file mode 100644
index 0000000000000000000000000000000000000000..4f120a8761ab0b9eaa519cb17a2561a4cc05a3a2
--- /dev/null
+++ b/drivers/misc/ltc3589.c
@@ -0,0 +1,1221 @@
+/*!***************************************************************************
+ *! FILE NAME : ltc3589.c
+ *! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator
+ *! Copyright (C) 2013 Elphel, Inc.
+ *! -----------------------------------------------------------------------------**
+ *!
+ *! This program is free software: you can redistribute it and/or modify
+ *! it under the terms of the GNU General Public License as published by
+ *! the Free Software Foundation, either version 3 of the License, or
+ *! (at your option) any later version.
+ *!
+ *! This program is distributed in the hope that it will be useful,
+ *! but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *! GNU General Public License for more details.
+ *!
+ *! You should have received a copy of the GNU General Public License
+ *! along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define DRV_VERSION "1.0"
+#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
+#define SYSFS_READONLY 0444
+#define SYSFS_WRITEONLY 0222
+
+#define CACHE_INIT 1
+#define CACHE_VOLAT 2
+#define LAST_REG 255
+
+
+#define AWE_SCR1 0x07ff
+#define AWE_SCR1_MODE_SD1 0x0703
+#define AWE_SCR1_MODE_SD2 0x070c
+#define AWE_SCR1_MODE_SD3 0x0730
+#define AWE_SCR1_MODE_BB 0x0740
+
+#define AWE_OVEN 0x10ff
+#define AWE_OVEN_EN_SD1 0x1001
+#define AWE_OVEN_EN_SD2 0x1002
+#define AWE_OVEN_EN_SD3 0x1004
+#define AWE_OVEN_EN_BB 0x1008
+#define AWE_OVEN_EN_LDO2 0x1010
+#define AWE_OVEN_EN_LDO3 0x1020
+#define AWE_OVEN_EN_LDO4 0x1040
+#define AWE_OVEN_ONLY 0x1080
+
+#define AWE_SCR2 0x12ff
+#define AWE_SCR2_NOWAIT_SD1 0x1201
+#define AWE_SCR2_NOWAIT_SD2 0x1202
+#define AWE_SCR2_NOWAIT_SD3 0x1204
+#define AWE_SCR2_NOWAIT_BB 0x1208
+#define AWE_SCR2_NOWAIT_LDO2 0x1210
+#define AWE_SCR2_NOWAIT_LDO3 0x1220
+#define AWE_SCR2_NOWAIT_LDO4 0x1240
+#define AWE_SCR2_PGOOD_SHTDN_INH 0x1280
+
+#define AWE_VCCR 0x20ff
+#define AWE_VCCR_SLEW_SD1 0x2001 /* self clearing bit */
+#define AWE_VCCR_REF_SEL_SD1 0x2002
+#define AWE_VCCR_SLEW_SD2 0x2004 /* self clearing bit */
+#define AWE_VCCR_REF_SEL_SD2 0x2008
+#define AWE_VCCR_SLEW_SD3 0x2010 /* self clearing bit */
+#define AWE_VCCR_REF_SEL_SD3 0x2020
+#define AWE_VCCR_SLEW_LDO2 0x2040 /* self clearing bit */
+#define AWE_VCCR_REF_SEL_LDO2 0x2080
+
+#define AWE_CLIRQ 0x21ff
+
+#define AWE_B1DTV1 0x23ff
+#define AWE_B1DTV1_REF 0x231f
+#define AWE_B1DTV1_PGMASK 0x2320
+#define AWE_B1DTV1_DVDT 0x23c0
+
+#define AWE_B1DTV2 0x24ff
+#define AWE_B1DTV2_REF 0x241f
+#define AWE_B1DTV2_CLKRATE 0x2420
+#define AWE_B1DTV2_PHASE 0x2440
+#define AWE_B1DTV2_KEEP_ALIVE 0x2480
+
+#define AWE_VRRCR 0x25ff
+#define AWE_VRRCR_SD1 0x2503
+#define AWE_VRRCR_SD2 0x250c
+#define AWE_VRRCR_SD3 0x2530
+#define AWE_VRRCR_LDO2 0x25c0
+
+#define AWE_B2DTV1 0x26ff
+#define AWE_B2DTV1_REF 0x261f
+#define AWE_B2DTV1_PGMASK 0x2620
+
+#define AWE_B2DTV2 0x27ff
+#define AWE_B2DTV2_REF 0x271f
+#define AWE_B2DTV2_CLKRATE 0x2720
+#define AWE_B2DTV2_PHASE 0x2740
+#define AWE_B2DTV2_KEEP_ALIVE 0x2780
+
+#define AWE_B3DTV1 0x29ff
+#define AWE_B3DTV1_REF 0x291f
+#define AWE_B3DTV1_PGMASK 0x2920
+
+#define AWE_B3DTV2 0x2aff
+#define AWE_B3DTV2_REF 0x2a1f
+#define AWE_B3DTV2_CLKRATE 0x2a20
+#define AWE_B3DTV2_PHASE 0x2a40
+#define AWE_B3DTV2_KEEP_ALIVE 0x2a80
+
+#define AWE_L2DTV1 0x32ff
+#define AWE_L2DTV1_REF 0x321f
+#define AWE_L2DTV1_PGMASK 0x3220
+#define AWE_L2DTV1_KEEP_ALIVE 0x3280
+
+#define AWE_L2DTV2 0x33ff
+#define AWE_L2DTV2_REF 0x331f
+#define AWE_L2DTV2_REF_LDO4 0x3360
+#define AWE_L2DTV2_MODE_LDO4 0x3380
+
+#define AWE_IRQSTAT 0x02ff
+#define AWE_IRQSTAT_PGOOD_TIMOUT 0x0208
+#define AWE_IRQSTAT_NEAR_UV 0x0210
+#define AWE_IRQSTAT_HARD_UV 0x0220
+#define AWE_IRQSTAT_NEAR_THERM 0x0240
+#define AWE_IRQSTAT_HARD_THERM 0x0280
+
+#define AWE_PGSTAT 0x13ff
+#define AWE_PGSTAT_LDO1 0x1301
+#define AWE_PGSTAT_SD1 0x1302
+#define AWE_PGSTAT_SD2 0x1304
+#define AWE_PGSTAT_SD3 0x1308
+#define AWE_PGSTAT_BB 0x1310
+#define AWE_PGSTAT_LDO2 0x1320
+#define AWE_PGSTAT_LDO3 0x1340
+#define AWE_PGSTAT_LDO4 0x1380
+
+
+#define LAST_REG 255
+
+struct ltc3589_cache_t {
+ u8 flags;
+ u8 data;
+};
+struct ltc3589_data_t {
+ int reg_addr; /* used for raw register r/w */
+ int simulate; /* do not perform actual i2c writes */
+ struct mutex lock;
+ struct ltc3589_cache_t cache[LAST_REG+1];
+};
+
+static struct i2c_device_id ltc3589_id[] = {
+ { "ltc3589", 0 },
+ { }
+};
+
+struct named_fields_t {
+ char *name;
+ u32 awe;
+};
+
+static const struct named_fields_t status_fields[]={
+ {"power_good", AWE_PGSTAT},
+ {"pgood_ldo1", AWE_PGSTAT_LDO1},
+ {"pgood_sd1", AWE_PGSTAT_SD1},
+ {"pgood_sd2", AWE_PGSTAT_SD2},
+ {"pgood_sd3", AWE_PGSTAT_SD3},
+ {"pgood_bb", AWE_PGSTAT_BB},
+ {"pgood_ldo2", AWE_PGSTAT_LDO2},
+ {"pgood_ldo3", AWE_PGSTAT_LDO3},
+ {"pgood_ldo4", AWE_PGSTAT_LDO4},
+ {"irqstat", AWE_IRQSTAT},
+ {"irqstat_pgoot_timeout",AWE_IRQSTAT_PGOOD_TIMOUT},
+ {"irqstat_near_uv", AWE_IRQSTAT_NEAR_UV},
+ {"irqstat_hard_uv", AWE_IRQSTAT_HARD_UV},
+ {"irqstat_near_therm", AWE_IRQSTAT_NEAR_THERM},
+ {"irqstat_hard_therm", AWE_IRQSTAT_HARD_THERM},
+};
+static const struct named_fields_t named_fields[]={
+ {"ref1_sd1", AWE_B1DTV1_REF},
+ {"ref2_sd1", AWE_B1DTV2_REF},
+ {"ref1_sd2", AWE_B2DTV1_REF},
+ {"ref2_sd2", AWE_B2DTV2_REF},
+ {"ref1_sd3", AWE_B3DTV1_REF},
+ {"ref2_sd3", AWE_B3DTV2_REF},
+ {"ref1_ldo2", AWE_L2DTV1_REF},
+ {"ref2_ldo2", AWE_L2DTV2_REF},
+ {"ref_ldo4", AWE_B1DTV1_REF},
+ {"dv_dt_sd1", AWE_B1DTV1_DVDT},
+ {"pgood_mask_sd1", AWE_B1DTV1_PGMASK},
+ {"pgood_mask_sd2", AWE_B2DTV1_PGMASK},
+ {"pgood_mask_sd3", AWE_B3DTV1_PGMASK},
+ {"pgood_mask_ldo21",AWE_L2DTV1_PGMASK},
+ {"clock_rate_sd1", AWE_B1DTV2_CLKRATE},
+ {"clock_rate_sd2", AWE_B2DTV2_CLKRATE},
+ {"clock_rate_sd3", AWE_B3DTV2_CLKRATE},
+ {"clock_phase_sd1", AWE_B1DTV2_PHASE},
+ {"clock_phase_sd2", AWE_B2DTV2_PHASE},
+ {"clock_phase_sd3", AWE_B3DTV2_PHASE},
+ {"keep_alive_sd1", AWE_B1DTV2_KEEP_ALIVE},
+ {"keep_alive_sd2", AWE_B2DTV2_KEEP_ALIVE},
+ {"keep_alive_sd3", AWE_B3DTV2_KEEP_ALIVE},
+ {"keep_alive_ldo2", AWE_L2DTV1_KEEP_ALIVE},
+ {"slew_rate_sd1", AWE_VRRCR_SD1},
+ {"slew_rate_sd2", AWE_VRRCR_SD2},
+ {"slew_rate_sd3", AWE_VRRCR_SD3},
+ {"slew_rate_ldo2", AWE_VRRCR_LDO2},
+ {"oven_ldo4", AWE_L2DTV2_MODE_LDO4},
+ {"oven_only", AWE_OVEN_ONLY},
+};
+
+static const int volatile_registers[]={AWE_IRQSTAT_PGOOD_TIMOUT, AWE_PGSTAT_LDO1, AWE_VCCR,-1};
+static const char * chn_names[]={"SD1","SD2","SD3","BB","LDO1","LDO2","LDO3","LDO4"};
+static const char * modes[]={"continuous","burst","pulse_skip","invalid"};
+static const char * pwr_states[]={"power_off","power_on"};
+static const char * wait_states[]={"no_wait", "wait"};
+static const char * reference_sel[]={"reference_sel1", "reference_sel2"};
+/* (register_address << 8) | mask */
+static const u32 register_masks[]= {
+ 0x07ff,0x12ff,0x20ff,0x24ff,0x25ff,0x26ff,0x27ff,
+ 0x29ff,0x2aff,0x32ff,0x33ff,0x10ff};
+static int make_status_fields(struct device *dev);
+static int make_bit_fields(struct device *dev);
+static ssize_t invalidate_cache_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 raw_address_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t power_wait_on_off_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t power_wait_on_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t pgood_timeout_inhibit_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t pgood_timeout_inhibit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t mode_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t reference_select_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t reference_select_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t reference_select_go_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t reference_select_go_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t field_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t get_field_value (struct device *dev, const char* name, char *buf, int newline);
+
+static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf);
+
+static int get_chn_mode(struct device *dev, char *buf, int chn); /* 0..3 */
+static int get_chn_pwr(struct device *dev, char *buf, int chn); /* 0..7 */
+static int get_chn_wait(struct device *dev, char *buf, int chn); /* 0..7 */
+static int get_ref_sel_go(struct device *dev, char *buf, int chn); /* 0..7 */
+
+static int no_off(const char *str);
+static int read_channel_mask(const char * str);
+static int read_field (struct i2c_client *client, u32 awe);
+static int write_field (struct i2c_client *client, u8 data, u32 awe);
+static int write_adwe(struct i2c_client *client, u32 adwe);
+static int write_reg(struct i2c_client *client, u8 reg, u8 val, u8 mask);
+static int read_reg(struct i2c_client *client, u8 reg);
+static void invalidate_cache(struct i2c_client *client);
+static int ltc3589_sysfs_register(struct device *dev);
+static void ltc3589_init_of(struct i2c_client *client);
+static int ltc3589_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id);
+static int ltc3589_i2c_remove(struct i2c_client *client);
+
+/* raw access to i2c registers, need to set address (9 bits) first, then r/w data */
+
+static DEVICE_ATTR(invalidate_cache, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, invalidate_cache_store);
+static DEVICE_ATTR(simulate, SYSFS_PERMISSIONS, simulate_show, simulate_store);
+static DEVICE_ATTR(address, SYSFS_PERMISSIONS, raw_address_show, raw_address_store);
+static DEVICE_ATTR(data, SYSFS_PERMISSIONS, raw_data_show, raw_data_store);
+static DEVICE_ATTR(hex_address, SYSFS_PERMISSIONS, raw_hex_address_show,raw_hex_address_store);
+static DEVICE_ATTR(hex_data, SYSFS_PERMISSIONS, raw_hex_data_show, raw_hex_data_store);
+static DEVICE_ATTR(hex_all, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_all_show, NULL);
+static DEVICE_ATTR(hex_adwe, SYSFS_PERMISSIONS, raw_hex_adwe_show, raw_hex_adwe_store);
+static DEVICE_ATTR(hex_adwe_help, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_adwe_help_show, NULL);
+
+
+static struct attribute *raw_dev_attrs[] = {
+ &dev_attr_invalidate_cache.attr,
+ &dev_attr_simulate.attr,
+ &dev_attr_address.attr,
+ &dev_attr_data.attr,
+ &dev_attr_hex_address.attr,
+ &dev_attr_hex_data.attr,
+ &dev_attr_hex_all.attr,
+ &dev_attr_hex_adwe.attr,
+ &dev_attr_hex_adwe_help.attr,
+ NULL
+};
+
+static const struct attribute_group dev_attr_raw_group = {
+ .attrs = raw_dev_attrs,
+ .name = "raw",
+};
+//static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf);
+//static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf);
+//static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+//static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf)
+
+static DEVICE_ATTR(power_good, SYSFS_PERMISSIONS & SYSFS_READONLY, pwr_bad_good_show, NULL);
+static DEVICE_ATTR(power_bad, SYSFS_PERMISSIONS & SYSFS_READONLY, pwr_bad_good_show, NULL);
+static DEVICE_ATTR(irq, SYSFS_PERMISSIONS, irq_show, irq_store);
+static DEVICE_ATTR(irq_txt, SYSFS_PERMISSIONS, irq_show_txt, irq_store);
+static DEVICE_ATTR(power_off, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
+static DEVICE_ATTR(power_on, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
+static DEVICE_ATTR(wait, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
+static DEVICE_ATTR(no_wait, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
+static DEVICE_ATTR(reference_sel1, SYSFS_PERMISSIONS, reference_select_show, reference_select_store);
+static DEVICE_ATTR(reference_sel2, SYSFS_PERMISSIONS, reference_select_show, reference_select_store);
+static DEVICE_ATTR(reference_sel_go, SYSFS_PERMISSIONS, reference_select_go_show, reference_select_go_store);
+static DEVICE_ATTR(continuous, SYSFS_PERMISSIONS, mode_show, mode_store);
+static DEVICE_ATTR(burst, SYSFS_PERMISSIONS, mode_show, mode_store);
+static DEVICE_ATTR(pulse_skip, SYSFS_PERMISSIONS, mode_show, mode_store);
+static DEVICE_ATTR(pgood_timeout_inhibit, SYSFS_PERMISSIONS, pgood_timeout_inhibit_show, pgood_timeout_inhibit_store);
+
+static struct attribute *control_dev_attrs[] = {
+ &dev_attr_power_good.attr,
+ &dev_attr_power_bad.attr,
+ &dev_attr_irq.attr,
+ &dev_attr_irq_txt.attr,
+ &dev_attr_power_off.attr,
+ &dev_attr_power_on.attr,
+ &dev_attr_wait.attr,
+ &dev_attr_no_wait.attr,
+ &dev_attr_reference_sel1.attr,
+ &dev_attr_reference_sel2.attr,
+ &dev_attr_reference_sel_go.attr,
+ &dev_attr_continuous.attr,
+ &dev_attr_burst.attr,
+ &dev_attr_pulse_skip.attr,
+ &dev_attr_pgood_timeout_inhibit.attr,
+ NULL
+};
+
+static const struct attribute_group dev_attr_control_group = {
+ .attrs = control_dev_attrs,
+ .name = "control",
+};
+//status_fields[]
+static int make_status_fields(struct device *dev)
+{
+ 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(status_fields)+1)*sizeof(pattrs[0]), GFP_KERNEL);
+ if (!pattrs) return -ENOMEM;
+ dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(status_fields)*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(status_fields)*sizeof(dev_attrs[0]));
+ memset(attr_group, 0, sizeof(*attr_group));
+ for (index=0;indexname = "status";
+ 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 int make_bit_fields(struct device *dev)
+{
+ 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(named_fields)+1)*sizeof(pattrs[0]), GFP_KERNEL);
+ if (!pattrs) return -ENOMEM;
+ dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(named_fields)*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(named_fields)*sizeof(dev_attrs[0]));
+ memset(attr_group, 0, sizeof(*attr_group));
+ for (index=0;indexname = "bit_fields";
+ 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 invalidate_cache_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ invalidate_cache(client);
+ return count;
+}
+
+static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(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 ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
+ sscanf(buf, "%du", &clientdata->simulate);
+ return count;
+}
+
+
+//clientdata->simulate
+static ssize_t raw_address_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%d\n",clientdata->reg_addr);
+}
+static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
+ sscanf(buf, "%du", &clientdata->reg_addr);
+ return count;
+}
+
+static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ int data= read_reg(client, clientdata->reg_addr);
+ return sprintf(buf, "%d\n",data);
+}
+static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ int data;
+ sscanf(buf, "%du", &data);
+ write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
+ return count;
+}
+
+static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "0x%02x\n",clientdata->reg_addr);
+}
+static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
+ sscanf(buf, "%x", &clientdata->reg_addr);
+ return count;
+}
+
+static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ int data= read_reg(client, clientdata->reg_addr);
+ return sprintf(buf, "0x%02x\n",data);
+}
+static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ int data;
+ sscanf(buf, "%x", &data);
+ write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
+ return count;
+}
+
+static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int low_addr=0,reg,data,rc,len=0, count=PAGE_SIZE;
+ struct i2c_client *client = to_i2c_client(dev);
+// struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ for (reg=low_addr;reg<=LAST_REG;reg++) if (count>10){
+ if ((reg & 0xf) ==0){
+ rc=sprintf(buf, "%02x: ",reg);
+ buf+=rc;
+ len+=rc;
+ count-=rc;
+ }
+ data= read_reg(client, reg); //ignore errors
+ if (data<0) rc=sprintf(buf, "??");
+ else rc=sprintf(buf, "%02x",data);
+ buf+=rc;
+ len+=rc;
+ count-=rc;
+ if (((reg & 0xf) == 0xf) || (reg==LAST_REG)){
+ rc=sprintf(buf, "\n");
+ } else {
+ rc=sprintf(buf, " ");
+ }
+ buf+=rc;
+ len+=rc;
+ count-=rc;
+ }
+ return len;
+}
+static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf,"Setting one/multiple registers with masks in the form [0x]AADDWW, where AA is register address\n" \
+ "DD - data byte and WW - write enable bits ( 1 - write, 0 - keep old)\n" \
+ "When read, provides current register data that can be used in device tree.\n");
+
+}
+
+//static const u32 register_masks[]= {
+static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int i,data;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ for (i=0;i>8)))<0) return data;
+ buf+=sprintf(buf," 0x%x",((register_masks[i] & 0x1ff00)<<8) | (register_masks[i] & 0xff) | ((data & 0xff)<<8));
+ if (((i+1) & 0x7)==0) buf+=sprintf(buf,"\n");
+ }
+ buf+=sprintf(buf,"\n");
+ return buf-cp;
+}
+
+/*
+ * accepts single or multiple data, each [0x]AAADDWW - AAA - register address, DD - data byte, WW - write enable mask (1 - write, 0 - keep).
+ * Ignores any other characters, so same format as in dts with hex data is OK
+ */
+static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ const char hex_digits[]="0123456789abcdefABCDEF";
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
+ int adwe,rc=0;
+ int left=count,num_bytes;
+ char * cp;
+ mutex_lock(&clientdata->lock);
+ while ((left>0) && ((cp=strpbrk(buf,hex_digits))) && cp[0]){
+ left -= (cp-buf);
+ buf = cp;
+ dev_dbg(dev,"left=%d", left);
+ sscanf(buf, "%x%n", &adwe,&num_bytes);
+ left-=num_bytes;
+ buf+=num_bytes;
+ dev_dbg(dev,"left=%d num_bytes=%d, adwe=0x%08x", left,num_bytes,adwe);
+ if (((rc=write_adwe(client, adwe)))<0) {
+ mutex_unlock(&clientdata->lock);
+ return rc;
+ }
+ }
+ mutex_unlock(&clientdata->lock);
+ return count;
+}
+
+//static const char * chn_names[]={"SD1","SD2","SD3","BB","LDO1","LDO2","LDO3","LDO4"};
+//static const char * pwr_states[]={"power_off","power_on"};
+//static const char * wait_states[]={"no_wait", "wait"};
+
+static ssize_t power_wait_on_off_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc,i,invert;
+ char * cp=buf;
+ u32 awe;
+ struct i2c_client *client = to_i2c_client(dev);
+ awe= ((strcmp(attr->attr.name,wait_states[0])==0) || (strcmp(attr->attr.name,wait_states[1])==0))?AWE_SCR2:AWE_OVEN;
+ invert=((strcmp(attr->attr.name,pwr_states[0])==0) || (strcmp(attr->attr.name,wait_states[0])==0))?0xff:0;
+ if (((rc=read_field(client,awe)))<0) return rc;
+ rc=((rc & 0xf) | 0x10 | ((rc & 0x70)<<1)) ^ invert;
+ for (i=0;i<8;i++) if (rc & (1<attr.name,wait_states[0])==0) || (strcmp(attr->attr.name,wait_states[1])==0))?AWE_SCR2:AWE_OVEN;
+ data=((strcmp(attr->attr.name,pwr_states[0])==0) || (strcmp(attr->attr.name,wait_states[0])==0))?0:0xff;
+ mask=read_channel_mask(buf);
+ mask=(mask & 0xf) | ((mask >> 1) & 0x70);
+ awe = (awe & 0xff00) | mask;
+ if (((rc=write_field (client, data, awe)))<0) return rc;
+ return count;
+}
+
+static ssize_t pgood_timeout_inhibit_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_SCR2_PGOOD_SHTDN_INH)))<0) return rc;
+ return sprintf(buf, "%d\n",rc);
+}
+static ssize_t pgood_timeout_inhibit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc,data;
+ struct i2c_client *client = to_i2c_client(dev);
+ sscanf(buf, "%d", &data);
+ if (((rc=write_field(client,data?1:0,AWE_SCR2_PGOOD_SHTDN_INH)))<0) return rc;
+ return count;
+}
+
+static ssize_t mode_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc,m,i;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_SCR1)))<0) return rc;
+ for (m=0;mattr.name,modes[m])) break;
+ if (m>=ARRAY_SIZE(modes)) return -EINVAL;
+ for (i=0;i<4;i+=2) if (((rc>>i) & 3) == m) {
+ if (buf!=cp) buf+=sprintf(buf," ");
+ buf+=sprintf(buf,"%s",chn_names[i]);
+ }
+ buf+=sprintf(buf,"\n");
+ return buf-cp;
+
+}
+
+static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc,mask,m,data;
+ u32 awe;
+ struct i2c_client *client = to_i2c_client(dev);
+ for (m=0;mattr.name,modes[m])) break;
+ if (m>=ARRAY_SIZE(modes)) return -EINVAL;
+ mask=read_channel_mask(buf);
+ mask=((mask & 1)? 3:0) | ((mask & 2)? 0xc:0) | ((mask & 4)? 0x30:0) | ((mask & 8)? 0x40:0);
+ awe = (AWE_SCR1 & 0xff00) | mask;
+ data= m | (m<<2) | (m<<4) | (m<<6);
+ if (((rc=write_field (client, data, awe)))<0) return rc;
+ return count;
+}
+
+static ssize_t reference_select_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc,m,i,chn;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_VCCR)))<0) return rc;
+ for (m=0;mattr.name,reference_sel[m])) break;
+ if (m>=ARRAY_SIZE(reference_sel)) return -EINVAL;
+ for (i=0;i<4;i++) if (((rc>>(2*i+1)) & 1)==m){
+ chn=i;
+ if (i==3) chn=5 ; /* LDO2 */
+ if (buf!=cp) buf+=sprintf(buf," ");
+ buf+=sprintf(buf,"%s",chn_names[chn]);
+/* if ((rc>>(2*i)) & 1){
+ buf+=sprintf(buf,"(slewing)");
+ }
+*/
+ }
+ buf+=sprintf(buf,"\n");
+ return buf-cp;
+}
+
+/* not raising go */
+static ssize_t reference_select_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc,mask,m,data;
+ u32 awe;
+ struct i2c_client *client = to_i2c_client(dev);
+ for (m=0;mattr.name,reference_sel[m])) break;
+ if (m>=ARRAY_SIZE(reference_sel)) return -EINVAL;
+ mask=read_channel_mask(buf);
+ mask=((mask & 1)? 2:0) | ((mask & 2)? 0x8:0) | ((mask & 4)? 0x20:0) | ((mask & 0x20)? 0x80:0);
+
+ awe = (AWE_VCCR & 0xff00) | mask;
+ data= m?0xff:0;
+ if (((rc=write_field (client, data, awe)))<0) return rc;
+ return count;
+}
+
+static ssize_t reference_select_go_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc,i,chn;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_VCCR)))<0) return rc;
+ for (i=0;i<4;i++) if ((rc>>(2*i)) & 1){
+ chn=i;
+ if (i==3) chn=5 ; /* LDO2 */
+ if (buf!=cp) buf+=sprintf(buf," ");
+ buf+=sprintf(buf,"%s",chn_names[chn]);
+ }
+ buf+=sprintf(buf,"\n");
+ return buf-cp;
+}
+
+static ssize_t reference_select_go_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc,mask,data;
+ u32 awe;
+ struct i2c_client *client = to_i2c_client(dev);
+ mask=read_channel_mask(buf);
+ mask=((mask & 1)? 1:0) | ((mask & 2)? 0x4:0) | ((mask & 4)? 0x10:0) | ((mask & 0x20)? 0x40:0);
+ awe = (AWE_VCCR & 0xff00) | mask;
+ data= 0xff;
+ if (((rc=write_field (client, data, awe)))<0) return rc;
+ return count;
+}
+//--------------------
+static ssize_t field_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return get_field_value (dev, attr->attr.name, buf, 1); /* with newline */
+}
+
+static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc,i;
+ int data;
+ struct i2c_client *client = to_i2c_client(dev);
+ for (i=0;iattr.name,named_fields[i].name)) break;
+ if (i>=ARRAY_SIZE(named_fields)) return -EINVAL;
+ sscanf(buf, "%du", &data);
+ if (((rc=write_field (client, data, named_fields[i].awe)))<0) return rc;
+ return count;
+}
+
+static ssize_t get_field_value (struct device *dev, const char* name, char *buf, int newline)
+{
+ int rc,i;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ for (i=0;i=ARRAY_SIZE(named_fields)) return -EINVAL;
+ if (((rc=read_field(client,named_fields[i].awe)))<0) return rc;
+ buf+=sprintf(buf,"%d",rc);
+ if (newline) buf+=sprintf(buf,"\n");
+ return buf-cp;
+}
+
+static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_IRQSTAT)))<0) return rc;
+ return sprintf(buf,"%d\n",rc);
+}
+
+static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_IRQSTAT)))<0) return rc;
+ buf += sprintf(buf,"0x%02x",rc);
+ if (rc & AWE_IRQSTAT_PGOOD_TIMOUT) buf += sprintf(buf,"PGOOD timeout");
+ if (rc & AWE_IRQSTAT_NEAR_UV) buf += sprintf(buf,", Near undervoltage");
+ if (rc & AWE_IRQSTAT_HARD_UV) buf += sprintf(buf,", Hard undervoltage");
+ if (rc & AWE_IRQSTAT_NEAR_THERM) buf += sprintf(buf,", Near undervoltage");
+ if (rc & AWE_IRQSTAT_HARD_THERM) buf += sprintf(buf,", Hard undervoltage");
+ buf += sprintf(buf,"\n");
+ return buf-cp;
+}
+
+static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=write_field (client, 0, AWE_CLIRQ)))<0) return rc;
+ return count;
+}
+static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int rc,i,pg;
+ char * cp=buf;
+ struct i2c_client *client = to_i2c_client(dev);
+ if (((rc=read_field(client,AWE_PGSTAT)))<0) return rc;
+ pg=(rc & 0xe0) | ((rc>>1) & 0xff) | ((rc << 4) & 0x10);
+
+ if (strstr(attr->attr.name,"bad")){
+ pg^=0xff;
+ }
+ for (i=0;i<8;i++) if (pg & (1<3)) return 0;
+ if (((rc=read_field(client,AWE_SCR1)))<0) return rc;
+ return sprintf (buf,"%s",modes[(rc >> (chn<<1)) &3]);
+}
+static int get_chn_pwr(struct device *dev, char *buf, int chn) /* 0..7 */
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if ((chn<0) || (chn>7)) return 0;
+ if (((rc=read_field(client,AWE_OVEN)))<0) return rc;
+ rc=(rc & 0xf) | 0x10 | ((rc & 0x70)<<1);
+ return sprintf (buf,"%s",pwr_states[(rc >> chn) & 1]);
+}
+static int get_chn_wait(struct device *dev, char *buf, int chn) /* 0..7 */
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if ((chn<0) || (chn>7)) return 0;
+ if (((rc=read_field(client,AWE_SCR2)))<0) return rc;
+ rc=(rc & 0xf) | 0x10 | ((rc & 0x70)<<1);
+ return sprintf (buf,"%s",wait_states[(rc >> chn) & 1]);
+}
+
+static int get_ref_sel_go(struct device *dev, char *buf, int chn) /* 0..7 */
+{
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev);
+ if ((chn<0) || ((chn>2) && (chn!=5))) return 0;
+ if (chn==5) chn=3;
+ if (((rc=read_field(client,AWE_VCCR)))<0) return rc;
+ rc =(rc >> (2*chn)) & 3;
+ return sprintf (buf,"%s%s",reference_sel[(rc>>1)&1],(rc&1)?" (slewing)":"");
+}
+
+static int no_off(const char *str)
+{
+ return strstr(str,"_off")?0:1;
+}
+
+static int read_channel_mask(const char * str)
+{
+ int mask =0, i;
+ for (i=0;i>8;
+ mask=awe&0xff;
+ if (mask!=0){
+ nshift=0;
+ while (((1<> nshift;
+ }
+ return 0;
+}
+
+
+static int write_field (struct i2c_client *client, u8 data, u32 awe)
+{
+ int rc,nshift;
+ u8 mask,reg_data;
+ u8 reg;
+ reg=awe>>8;
+ mask=awe&0xff;
+ if (mask!=0){
+ nshift=0;
+ while (((1<>8) & 0xff;
+ u16 reg= (adwe>>16) & 0xff;
+ return write_reg(client, reg, data, we);
+}
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 val, u8 mask)
+{
+ int rc;
+ struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
+ if (reg>LAST_REG) return -EINVAL;
+ if (mask==0) return 0;
+ dev_dbg(&client->dev,"reg=0x%x, val=0x%x, mask=0x%x\n", (int) reg, (int) val, (int) mask);
+ if (mask !=0xff){
+ if (((rc=read_reg(client, reg)))<0) return rc;
+ val=((val ^ rc) & mask)^ rc;
+ if ((val==rc) && !(clientdata->cache[reg].flags & CACHE_VOLAT)) {
+ dev_dbg(&client->dev,"No change and not volatile -> no write\n");
+ return 0;
+ }
+ }
+ clientdata->cache[reg].data= val;
+ clientdata->cache[reg].flags |= CACHE_INIT;
+ if (clientdata->simulate){
+ dev_info(&client->dev,">>> Simulating LTC3589 register write: 0x%02x->[0x%02x]\n",(int) reg, (int) val);
+ return 0;
+ }
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+
+static int read_reg(struct i2c_client *client, u8 reg)
+{
+ int rc;
+ struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
+ if (reg>LAST_REG) return -EINVAL;
+ if (clientdata && (clientdata->cache[reg].flags & CACHE_INIT) && !(clientdata->cache[reg].flags & CACHE_VOLAT)){
+ dev_dbg(&client->dev,"Using cached register: reg=0x%x -> 0x%x\n",reg,(int) clientdata->cache[reg].data);
+ return clientdata->cache[reg].data;
+ }
+ rc= i2c_smbus_read_byte_data(client, reg & 0xff);
+ dev_dbg(&client->dev,"reading i2c device : slave=0x%x, reg=0x%x -> 0x%x\n",(int) (client->addr),reg,rc);
+ if (rc<0) return rc;
+ if (clientdata){
+ clientdata->cache[reg].data= (u8) rc;
+ clientdata->cache[reg].flags |= CACHE_INIT;
+ }
+ return rc;
+}
+static void invalidate_cache(struct i2c_client *client)
+{
+ int i;
+ struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
+ for (i=0;i<=LAST_REG;i++){
+ clientdata->cache[i].flags&= ~CACHE_INIT;
+ }
+}
+
+static int ltc3589_sysfs_register(struct device *dev)
+{
+ int retval=0;
+ if (&dev->kobj) {
+ if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_raw_group)))<0) return retval;
+ if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_control_group)))<0) return retval;
+ if (((retval = make_status_fields (dev)))<0) return retval;
+ if (((retval = make_bit_fields (dev)))<0) return retval;
+ }
+ return retval;
+}
+
+static void ltc3589_init_of(struct i2c_client *client)
+{
+ // struct device *dev=&client->dev;
+ const __be32 * config_data;
+ const char * init_type_string;
+ int init_type=0; /* 0 - none, 1 - always, 2 - if not running (TODO) */
+ struct device_node *node = client->dev.of_node;
+ int len,i,n;
+ u16 page_reg;
+ char buf[40];
+ u64 freq[3];
+
+ struct ltc3589_setup_data {
+ u8 page;
+ u8 reg;
+ u8 data;
+ u8 mask;
+ };
+ struct ltc3589_setup_data setup_data;
+ __be32 * setup_data_be32= (__be32 *) &setup_data;
+#if 0
+ if (node) {
+ init_type_string = of_get_property(client->dev.of_node, "ltc3589,init", &len);
+ if (init_type_string){
+ if (strcmp(init_type_string,"always")==0) init_type=1;
+ else if (strcmp(init_type_string,"if off")==0) init_type=2;
+ else {
+ dev_err(&client->dev,"Unrecognized ltc3589 initialization type '%s'. Only 'always' and 'if off' are permitted\n",init_type_string);
+ }
+ }
+ switch (init_type){
+ case 2:
+ // static int is_set_up(struct i2c_client *client);
+ i=is_set_up(client);
+ if (i<0){
+ dev_err(&client->dev,"Error reading i2c register, aborting initialization\n");
+ return;
+ } else if (i>0){
+ init_type=0;
+ dev_info(&client->dev,"Skipping conditional initialization (some driver variables will not be initialized)\n");
+ return;
+ }
+ init_type=1;
+ /* falling to initialization */
+ case 1:
+ pre_init(client,1); // clear outputs and muxes - they will be programmed later
+ break;
+ }
+
+ config_data = of_get_property(client->dev.of_node, "ltc3589,configuration_data", &len);
+ if (config_data){
+ len /= sizeof(*config_data);
+ dev_dbg(&client->dev,"Read %d values\n",len);
+ dev_info(&client->dev,"Found %d items in 'ltc3589,configuration_data' in the Device Tree\n",len);
+ for (i=0;idev,"page_reg=0x%03x, data=0x%02x, mask=0x%02x \n",
+ (int) page_reg,(int)setup_data.data,(int)setup_data.mask);
+ if (write_reg(client, page_reg, setup_data.data, setup_data.mask)<0) return;
+ }
+ }
+ /* input section */
+ /* setting input frequency here divides (if needed) and feeds it to the PLL reference. Other variants can use raw register writes */
+ for (n=0;in_freq_names[n];n++){
+ sprintf(buf,"ltc3589,%s",in_freq_names[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data && (len>0)){
+ dev_info(&client->dev,"Found '%s', value = %d (0x%x)\n",buf,(int)(be32_to_cpup(config_data)),(int)(be32_to_cpup(config_data)));
+ if (set_in_frequency(client, be32_to_cpup(config_data),n)<0) return; /* 32 bits are sufficient here */
+ }
+ }
+ /* setting PLL for the most important output frequency, sets analog parameters accordingly. Assumes input frequency set above */
+ for (n=0;pll_setup_names[n];n++){
+ sprintf(buf,"ltc3589,%s",pll_setup_names[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data && (len>0)){
+ len /= sizeof(*config_data);
+ freq[0]=be32_to_cpup(config_data);
+ if (len<3){
+ freq[1]=0;
+ freq[2]=1;
+ } else {
+ freq[1]=be32_to_cpup(&config_data[1]);
+ freq[2]=be32_to_cpup(&config_data[2]);
+ }
+ dev_info(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
+ if (n & 2){ /* by output */
+ if (set_pll_freq_by_out(client, freq, n & 1)<0) return;
+ } else { /* directly set PLL frequency */
+ if (set_pll_freq (client, freq, n & 1)<0) return;
+ }
+ if (set_pll_paremeters(client)<0) return;
+/* if (set_misc_registers(client)<0) return; */ /* moved to pre_init() */
+ }
+ }
+ /* setting MSn dividers (same channel as output), powering them up, setting output dividers and routing outputs */
+ for (n=0;out_freq_setup_names[n];n++){
+ sprintf(buf,"ltc3589,%s",out_freq_setup_names[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data && (len>0)){
+ len /= sizeof(*config_data);
+ freq[0]=be32_to_cpup(config_data);
+ if (len<3){
+ freq[1]=0;
+ freq[2]=1;
+ } else {
+ freq[1]=be32_to_cpup(&config_data[1]);
+ freq[2]=be32_to_cpup(&config_data[2]);
+ }
+ dev_info(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
+ if (set_out_frequency_and_route(client, freq, n&3, n>>2)<0) return;
+ }
+ }
+ /* configure output driver standard */
+ for (n=0;drv_configs[n].description;n++){
+ sprintf(buf,"ltc3589,%s",drv_configs[n].description);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data){
+ len /= sizeof(*config_data);
+ for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
+ if (configure_output_driver(&client->dev, drv_configs[n].description, setup_data.mask)<0) return;
+ }
+ }
+ }
+
+ /* configure disabled state of the output(s) */
+ for (n=0;out_dis_states[n];n++){
+ sprintf(buf,"ltc3589,%s",out_dis_states[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data){
+ len /= sizeof(*config_data);
+ for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
+ if (set_drv_disable(client, n, setup_data.mask)<0) return;
+ }
+ }
+ }
+
+ /* configure powerdown state of the output(s) */
+ for (n=0;out_pwr_states[n];n++){
+ sprintf(buf,"ltc3589,%s",out_pwr_states[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data){
+ len /= sizeof(*config_data);
+ for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
+ if (set_drv_powerdown(client, n, setup_data.mask)<0) return;
+ }
+ }
+ }
+
+ /* configure output enable state of the output(s) */
+ for (n=0;out_en_states[n];n++){
+ sprintf(buf,"ltc3589,%s",out_en_states[n]);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data){
+ len /= sizeof(*config_data);
+ for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
+ if (set_drv_disable(client, n, setup_data.mask)<0) return;
+ }
+ }
+ }
+ /* setting spread spectrum parameters */
+ for (n=0;n<4;n++){
+ sprintf(buf,"ltc3589,spread_spectrum_%d",n);
+ config_data = of_get_property(client->dev.of_node, buf, &len);
+ if (config_data && (len>0)){
+ len /= sizeof(*config_data);
+ rate=get_ss_down_rate(client, n);
+ amp= get_ss_down_amplitude(client, n);
+ if (len>1) amp = be32_to_cpup(&config_data[1]);
+ if (len>2) rate = be32_to_cpup(&config_data[2]);
+ if (store_ss_down_parameters(client, rate, amp, n)==0){
+ dev_info(&client->dev,"Set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
+ n,amp,rate,config_data[0]?"ON":"OFF");
+ } else {
+ dev_err(&client->dev,"Failed to set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
+ n,amp,rate,config_data[0]?"ON":"OFF");
+ continue;
+ }
+ if (config_data[0]){ /* enable SS */
+ if ((set_ss_down(client, n)==0) && /* calculate and set SS registers */
+ (set_ss_state(client, 1, n)==0)){ // enable SS. Not using enable_spread_spectrum() as we'll reset MS later anyway
+ dev_info(&client->dev,"Spread spectrum enabled for MS%d\n",n);
+ } else {
+ dev_info(&client->dev,"Fail to enable spread spectrum for MS%d\n",n);
+ }
+ }
+ }
+ }
+ } else {
+ dev_info(&client->dev,"Device tree data not found for %s\n",client->name);
+ }
+ if (init_type){
+ if (post_init(client,INIT_TIMEOUT)<0) dev_err(&client->dev,"ltc3589 initialization failed\n");
+ else dev_info(&client->dev,"ltc3589 initialized\n");
+ }
+#endif
+}
+
+static int ltc3589_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int i,rc=0;
+ struct ltc3589_data_t *clientdata = NULL;
+ /* initialize i2c ... */
+ if (read_field (client, AWE_IRQSTAT_PGOOD_TIMOUT)<0) {
+ dev_err(&client->dev, "%s: chip not detected\n",id->name);
+ return -EIO;
+ }
+ dev_info(&client->dev,
+ "Chip %s is found, driver version %s\n", id->name, DRV_VERSION);
+ clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
+ for (i=0;i<=LAST_REG;i++){
+ clientdata->cache[i].flags=0;
+ clientdata->cache[i].data=0;
+ }
+ for (i=0;volatile_registers[i]>=0;i++){
+ clientdata->cache[volatile_registers[i]>>8].flags |= CACHE_VOLAT;
+ }
+ clientdata->simulate=0;
+ clientdata->reg_addr=0;
+ //volatile_registers[]
+ i2c_set_clientdata(client, clientdata);
+ ltc3589_sysfs_register(&client->dev);
+ mutex_init(&clientdata->lock);
+ ltc3589_init_of(client);
+ return 0;
+}
+
+
+static int ltc3589_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+static struct i2c_driver ltc3589_i2c_driver = {
+ .driver = {
+ .name = "ltc3589",
+ .owner = THIS_MODULE,
+ },
+ .probe = ltc3589_i2c_probe,
+ .remove = ltc3589_i2c_remove,
+ .id_table = ltc3589_id,
+};
+module_i2c_driver(ltc3589_i2c_driver);
+MODULE_DEVICE_TABLE(i2c, ltc3589_id);
+MODULE_AUTHOR("Andrey Filippov ");
+MODULE_DESCRIPTION("LTC3589 I2C bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:ltc3589");