162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Windfarm PowerMac thermal control. SMU "satellite" controller sensors. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/wait.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/mutex.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/smu.h> 1862306a36Sopenharmony_ci#include <asm/pmac_low_i2c.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "windfarm.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define VERSION "1.0" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* If the cache is older than 800ms we'll refetch it */ 2562306a36Sopenharmony_ci#define MAX_AGE msecs_to_jiffies(800) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct wf_sat { 2862306a36Sopenharmony_ci struct kref ref; 2962306a36Sopenharmony_ci int nr; 3062306a36Sopenharmony_ci struct mutex mutex; 3162306a36Sopenharmony_ci unsigned long last_read; /* jiffies when cache last updated */ 3262306a36Sopenharmony_ci u8 cache[16]; 3362306a36Sopenharmony_ci struct list_head sensors; 3462306a36Sopenharmony_ci struct i2c_client *i2c; 3562306a36Sopenharmony_ci struct device_node *node; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct wf_sat *sats[2]; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct wf_sat_sensor { 4162306a36Sopenharmony_ci struct list_head link; 4262306a36Sopenharmony_ci int index; 4362306a36Sopenharmony_ci int index2; /* used for power sensors */ 4462306a36Sopenharmony_ci int shift; 4562306a36Sopenharmony_ci struct wf_sat *sat; 4662306a36Sopenharmony_ci struct wf_sensor sens; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, 5262306a36Sopenharmony_ci unsigned int *size) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct wf_sat *sat; 5562306a36Sopenharmony_ci int err; 5662306a36Sopenharmony_ci unsigned int i, len; 5762306a36Sopenharmony_ci u8 *buf; 5862306a36Sopenharmony_ci u8 data[4]; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* TODO: Add the resulting partition to the device-tree */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (sat_id > 1 || (sat = sats[sat_id]) == NULL) 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8); 6662306a36Sopenharmony_ci if (err) { 6762306a36Sopenharmony_ci printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err); 6862306a36Sopenharmony_ci return NULL; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci err = i2c_smbus_read_word_data(sat->i2c, 9); 7262306a36Sopenharmony_ci if (err < 0) { 7362306a36Sopenharmony_ci printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n"); 7462306a36Sopenharmony_ci return NULL; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci len = err; 7762306a36Sopenharmony_ci if (len == 0) { 7862306a36Sopenharmony_ci printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id); 7962306a36Sopenharmony_ci return NULL; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci len = le16_to_cpu(len); 8362306a36Sopenharmony_ci len = (len + 3) & ~3; 8462306a36Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 8562306a36Sopenharmony_ci if (buf == NULL) 8662306a36Sopenharmony_ci return NULL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (i = 0; i < len; i += 4) { 8962306a36Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data); 9062306a36Sopenharmony_ci if (err < 0) { 9162306a36Sopenharmony_ci printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n", 9262306a36Sopenharmony_ci err); 9362306a36Sopenharmony_ci goto fail; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci buf[i] = data[1]; 9662306a36Sopenharmony_ci buf[i+1] = data[0]; 9762306a36Sopenharmony_ci buf[i+2] = data[3]; 9862306a36Sopenharmony_ci buf[i+3] = data[2]; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci printk(KERN_DEBUG "sat %d partition %x:", sat_id, id); 10262306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET, 10362306a36Sopenharmony_ci 16, 1, buf, len, false); 10462306a36Sopenharmony_ci if (size) 10562306a36Sopenharmony_ci *size = len; 10662306a36Sopenharmony_ci return (struct smu_sdbp_header *) buf; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci fail: 10962306a36Sopenharmony_ci kfree(buf); 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* refresh the cache */ 11562306a36Sopenharmony_cistatic int wf_sat_read_cache(struct wf_sat *sat) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache); 12062306a36Sopenharmony_ci if (err < 0) 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci sat->last_read = jiffies; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#ifdef LOTSA_DEBUG 12562306a36Sopenharmony_ci { 12662306a36Sopenharmony_ci int i; 12762306a36Sopenharmony_ci printk(KERN_DEBUG "wf_sat_get: data is"); 12862306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET, 12962306a36Sopenharmony_ci 16, 1, sat->cache, 16, false); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci#endif 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct wf_sat_sensor *sens = wf_to_sat(sr); 13862306a36Sopenharmony_ci struct wf_sat *sat = sens->sat; 13962306a36Sopenharmony_ci int i, err; 14062306a36Sopenharmony_ci s32 val; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (sat->i2c == NULL) 14362306a36Sopenharmony_ci return -ENODEV; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci mutex_lock(&sat->mutex); 14662306a36Sopenharmony_ci if (time_after(jiffies, (sat->last_read + MAX_AGE))) { 14762306a36Sopenharmony_ci err = wf_sat_read_cache(sat); 14862306a36Sopenharmony_ci if (err) 14962306a36Sopenharmony_ci goto fail; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci i = sens->index * 2; 15362306a36Sopenharmony_ci val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift; 15462306a36Sopenharmony_ci if (sens->index2 >= 0) { 15562306a36Sopenharmony_ci i = sens->index2 * 2; 15662306a36Sopenharmony_ci /* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */ 15762306a36Sopenharmony_ci val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci *value = val; 16162306a36Sopenharmony_ci err = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci fail: 16462306a36Sopenharmony_ci mutex_unlock(&sat->mutex); 16562306a36Sopenharmony_ci return err; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void wf_sat_release(struct kref *ref) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct wf_sat *sat = container_of(ref, struct wf_sat, ref); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (sat->nr >= 0) 17362306a36Sopenharmony_ci sats[sat->nr] = NULL; 17462306a36Sopenharmony_ci of_node_put(sat->node); 17562306a36Sopenharmony_ci kfree(sat); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void wf_sat_sensor_release(struct wf_sensor *sr) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct wf_sat_sensor *sens = wf_to_sat(sr); 18162306a36Sopenharmony_ci struct wf_sat *sat = sens->sat; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci kfree(sens); 18462306a36Sopenharmony_ci kref_put(&sat->ref, wf_sat_release); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct wf_sensor_ops wf_sat_ops = { 18862306a36Sopenharmony_ci .get_value = wf_sat_sensor_get, 18962306a36Sopenharmony_ci .release = wf_sat_sensor_release, 19062306a36Sopenharmony_ci .owner = THIS_MODULE, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int wf_sat_probe(struct i2c_client *client) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct device_node *dev = client->dev.of_node; 19662306a36Sopenharmony_ci struct wf_sat *sat; 19762306a36Sopenharmony_ci struct wf_sat_sensor *sens; 19862306a36Sopenharmony_ci const u32 *reg; 19962306a36Sopenharmony_ci const char *loc; 20062306a36Sopenharmony_ci u8 chip, core; 20162306a36Sopenharmony_ci struct device_node *child; 20262306a36Sopenharmony_ci int shift, cpu, index; 20362306a36Sopenharmony_ci char *name; 20462306a36Sopenharmony_ci int vsens[2], isens[2]; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL); 20762306a36Sopenharmony_ci if (sat == NULL) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci sat->nr = -1; 21062306a36Sopenharmony_ci sat->node = of_node_get(dev); 21162306a36Sopenharmony_ci kref_init(&sat->ref); 21262306a36Sopenharmony_ci mutex_init(&sat->mutex); 21362306a36Sopenharmony_ci sat->i2c = client; 21462306a36Sopenharmony_ci INIT_LIST_HEAD(&sat->sensors); 21562306a36Sopenharmony_ci i2c_set_clientdata(client, sat); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci vsens[0] = vsens[1] = -1; 21862306a36Sopenharmony_ci isens[0] = isens[1] = -1; 21962306a36Sopenharmony_ci for_each_child_of_node(dev, child) { 22062306a36Sopenharmony_ci reg = of_get_property(child, "reg", NULL); 22162306a36Sopenharmony_ci loc = of_get_property(child, "location", NULL); 22262306a36Sopenharmony_ci if (reg == NULL || loc == NULL) 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* the cooked sensors are between 0x30 and 0x37 */ 22662306a36Sopenharmony_ci if (*reg < 0x30 || *reg > 0x37) 22762306a36Sopenharmony_ci continue; 22862306a36Sopenharmony_ci index = *reg - 0x30; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* expect location to be CPU [AB][01] ... */ 23162306a36Sopenharmony_ci if (strncmp(loc, "CPU ", 4) != 0) 23262306a36Sopenharmony_ci continue; 23362306a36Sopenharmony_ci chip = loc[4] - 'A'; 23462306a36Sopenharmony_ci core = loc[5] - '0'; 23562306a36Sopenharmony_ci if (chip > 1 || core > 1) { 23662306a36Sopenharmony_ci printk(KERN_ERR "wf_sat_create: don't understand " 23762306a36Sopenharmony_ci "location %s for %pOF\n", loc, child); 23862306a36Sopenharmony_ci continue; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci cpu = 2 * chip + core; 24162306a36Sopenharmony_ci if (sat->nr < 0) 24262306a36Sopenharmony_ci sat->nr = chip; 24362306a36Sopenharmony_ci else if (sat->nr != chip) { 24462306a36Sopenharmony_ci printk(KERN_ERR "wf_sat_create: can't cope with " 24562306a36Sopenharmony_ci "multiple CPU chips on one SAT (%s)\n", loc); 24662306a36Sopenharmony_ci continue; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (of_node_is_type(child, "voltage-sensor")) { 25062306a36Sopenharmony_ci name = "cpu-voltage"; 25162306a36Sopenharmony_ci shift = 4; 25262306a36Sopenharmony_ci vsens[core] = index; 25362306a36Sopenharmony_ci } else if (of_node_is_type(child, "current-sensor")) { 25462306a36Sopenharmony_ci name = "cpu-current"; 25562306a36Sopenharmony_ci shift = 8; 25662306a36Sopenharmony_ci isens[core] = index; 25762306a36Sopenharmony_ci } else if (of_node_is_type(child, "temp-sensor")) { 25862306a36Sopenharmony_ci name = "cpu-temp"; 25962306a36Sopenharmony_ci shift = 10; 26062306a36Sopenharmony_ci } else 26162306a36Sopenharmony_ci continue; /* hmmm shouldn't happen */ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* the +16 is enough for "cpu-voltage-n" */ 26462306a36Sopenharmony_ci sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL); 26562306a36Sopenharmony_ci if (sens == NULL) { 26662306a36Sopenharmony_ci printk(KERN_ERR "wf_sat_create: couldn't create " 26762306a36Sopenharmony_ci "%s sensor %d (no memory)\n", name, cpu); 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci sens->index = index; 27162306a36Sopenharmony_ci sens->index2 = -1; 27262306a36Sopenharmony_ci sens->shift = shift; 27362306a36Sopenharmony_ci sens->sat = sat; 27462306a36Sopenharmony_ci sens->sens.ops = &wf_sat_ops; 27562306a36Sopenharmony_ci sens->sens.name = (char *) (sens + 1); 27662306a36Sopenharmony_ci snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (wf_register_sensor(&sens->sens)) 27962306a36Sopenharmony_ci kfree(sens); 28062306a36Sopenharmony_ci else { 28162306a36Sopenharmony_ci list_add(&sens->link, &sat->sensors); 28262306a36Sopenharmony_ci kref_get(&sat->ref); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* make the power sensors */ 28762306a36Sopenharmony_ci for (core = 0; core < 2; ++core) { 28862306a36Sopenharmony_ci if (vsens[core] < 0 || isens[core] < 0) 28962306a36Sopenharmony_ci continue; 29062306a36Sopenharmony_ci cpu = 2 * sat->nr + core; 29162306a36Sopenharmony_ci sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL); 29262306a36Sopenharmony_ci if (sens == NULL) { 29362306a36Sopenharmony_ci printk(KERN_ERR "wf_sat_create: couldn't create power " 29462306a36Sopenharmony_ci "sensor %d (no memory)\n", cpu); 29562306a36Sopenharmony_ci continue; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci sens->index = vsens[core]; 29862306a36Sopenharmony_ci sens->index2 = isens[core]; 29962306a36Sopenharmony_ci sens->shift = 0; 30062306a36Sopenharmony_ci sens->sat = sat; 30162306a36Sopenharmony_ci sens->sens.ops = &wf_sat_ops; 30262306a36Sopenharmony_ci sens->sens.name = (char *) (sens + 1); 30362306a36Sopenharmony_ci snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (wf_register_sensor(&sens->sens)) 30662306a36Sopenharmony_ci kfree(sens); 30762306a36Sopenharmony_ci else { 30862306a36Sopenharmony_ci list_add(&sens->link, &sat->sensors); 30962306a36Sopenharmony_ci kref_get(&sat->ref); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (sat->nr >= 0) 31462306a36Sopenharmony_ci sats[sat->nr] = sat; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void wf_sat_remove(struct i2c_client *client) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct wf_sat *sat = i2c_get_clientdata(client); 32262306a36Sopenharmony_ci struct wf_sat_sensor *sens; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* release sensors */ 32562306a36Sopenharmony_ci while(!list_empty(&sat->sensors)) { 32662306a36Sopenharmony_ci sens = list_first_entry(&sat->sensors, 32762306a36Sopenharmony_ci struct wf_sat_sensor, link); 32862306a36Sopenharmony_ci list_del(&sens->link); 32962306a36Sopenharmony_ci wf_unregister_sensor(&sens->sens); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci sat->i2c = NULL; 33262306a36Sopenharmony_ci kref_put(&sat->ref, wf_sat_release); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const struct i2c_device_id wf_sat_id[] = { 33662306a36Sopenharmony_ci { "MAC,smu-sat", 0 }, 33762306a36Sopenharmony_ci { } 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wf_sat_id); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct of_device_id wf_sat_of_id[] = { 34262306a36Sopenharmony_ci { .compatible = "smu-sat", }, 34362306a36Sopenharmony_ci { } 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wf_sat_of_id); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic struct i2c_driver wf_sat_driver = { 34862306a36Sopenharmony_ci .driver = { 34962306a36Sopenharmony_ci .name = "wf_smu_sat", 35062306a36Sopenharmony_ci .of_match_table = wf_sat_of_id, 35162306a36Sopenharmony_ci }, 35262306a36Sopenharmony_ci .probe = wf_sat_probe, 35362306a36Sopenharmony_ci .remove = wf_sat_remove, 35462306a36Sopenharmony_ci .id_table = wf_sat_id, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cimodule_i2c_driver(wf_sat_driver); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 36062306a36Sopenharmony_ciMODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); 36162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 362