18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Windfarm PowerMac thermal control. AD7417 sensors 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/wait.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <asm/prom.h> 178c2ecf20Sopenharmony_ci#include <asm/machdep.h> 188c2ecf20Sopenharmony_ci#include <asm/io.h> 198c2ecf20Sopenharmony_ci#include <asm/sections.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "windfarm.h" 228c2ecf20Sopenharmony_ci#include "windfarm_mpu.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define VERSION "1.0" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct wf_ad7417_priv { 278c2ecf20Sopenharmony_ci struct kref ref; 288c2ecf20Sopenharmony_ci struct i2c_client *i2c; 298c2ecf20Sopenharmony_ci u8 config; 308c2ecf20Sopenharmony_ci u8 cpu; 318c2ecf20Sopenharmony_ci const struct mpu_data *mpu; 328c2ecf20Sopenharmony_ci struct wf_sensor sensors[5]; 338c2ecf20Sopenharmony_ci struct mutex lock; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv = sr->priv; 398c2ecf20Sopenharmony_ci u8 buf[2]; 408c2ecf20Sopenharmony_ci s16 raw; 418c2ecf20Sopenharmony_ci int rc; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci *value = 0; 448c2ecf20Sopenharmony_ci mutex_lock(&pv->lock); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Read temp register */ 478c2ecf20Sopenharmony_ci buf[0] = 0; 488c2ecf20Sopenharmony_ci rc = i2c_master_send(pv->i2c, buf, 1); 498c2ecf20Sopenharmony_ci if (rc < 0) 508c2ecf20Sopenharmony_ci goto error; 518c2ecf20Sopenharmony_ci rc = i2c_master_recv(pv->i2c, buf, 2); 528c2ecf20Sopenharmony_ci if (rc < 0) 538c2ecf20Sopenharmony_ci goto error; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* Read a a 16-bit signed value */ 568c2ecf20Sopenharmony_ci raw = be16_to_cpup((__le16 *)buf); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Convert 8.8-bit to 16.16 fixed point */ 598c2ecf20Sopenharmony_ci *value = ((s32)raw) << 8; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci mutex_unlock(&pv->lock); 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cierror: 658c2ecf20Sopenharmony_ci mutex_unlock(&pv->lock); 668c2ecf20Sopenharmony_ci return -1; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Scaling factors for the AD7417 ADC converters (except 718c2ecf20Sopenharmony_ci * for the CPU diode which is obtained from the EEPROM). 728c2ecf20Sopenharmony_ci * Those values are obtained from the property list of 738c2ecf20Sopenharmony_ci * the darwin driver 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */ 768c2ecf20Sopenharmony_ci#define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */ 778c2ecf20Sopenharmony_ci#define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv, 808c2ecf20Sopenharmony_ci int chan, s32 raw, s32 *value) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci switch(chan) { 838c2ecf20Sopenharmony_ci case 1: /* Diode */ 848c2ecf20Sopenharmony_ci *value = (raw * (s32)pv->mpu->mdiode + 858c2ecf20Sopenharmony_ci ((s32)pv->mpu->bdiode << 12)) >> 2; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case 2: /* 12v current */ 888c2ecf20Sopenharmony_ci *value = raw * ADC_12V_CURRENT_SCALE; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case 3: /* core voltage */ 918c2ecf20Sopenharmony_ci *value = raw * ADC_CPU_VOLTAGE_SCALE; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci case 4: /* core current */ 948c2ecf20Sopenharmony_ci *value = raw * ADC_CPU_CURRENT_SCALE; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv = sr->priv; 1028c2ecf20Sopenharmony_ci int chan = sr - pv->sensors; 1038c2ecf20Sopenharmony_ci int i, rc; 1048c2ecf20Sopenharmony_ci u8 buf[2]; 1058c2ecf20Sopenharmony_ci u16 raw; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci *value = 0; 1088c2ecf20Sopenharmony_ci mutex_lock(&pv->lock); 1098c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1108c2ecf20Sopenharmony_ci /* Set channel */ 1118c2ecf20Sopenharmony_ci buf[0] = 1; 1128c2ecf20Sopenharmony_ci buf[1] = (pv->config & 0x1f) | (chan << 5); 1138c2ecf20Sopenharmony_ci rc = i2c_master_send(pv->i2c, buf, 2); 1148c2ecf20Sopenharmony_ci if (rc < 0) 1158c2ecf20Sopenharmony_ci goto error; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Wait for conversion */ 1188c2ecf20Sopenharmony_ci msleep(1); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Switch to data register */ 1218c2ecf20Sopenharmony_ci buf[0] = 4; 1228c2ecf20Sopenharmony_ci rc = i2c_master_send(pv->i2c, buf, 1); 1238c2ecf20Sopenharmony_ci if (rc < 0) 1248c2ecf20Sopenharmony_ci goto error; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Read result */ 1278c2ecf20Sopenharmony_ci rc = i2c_master_recv(pv->i2c, buf, 2); 1288c2ecf20Sopenharmony_ci if (rc < 0) 1298c2ecf20Sopenharmony_ci goto error; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Read a a 16-bit signed value */ 1328c2ecf20Sopenharmony_ci raw = be16_to_cpup((__le16 *)buf) >> 6; 1338c2ecf20Sopenharmony_ci wf_ad7417_adc_convert(pv, chan, raw, value); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]" 1368c2ecf20Sopenharmony_ci " raw value: 0x%x, conv to: 0x%08x\n", 1378c2ecf20Sopenharmony_ci chan, sr->name, raw, *value); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci mutex_unlock(&pv->lock); 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci error: 1438c2ecf20Sopenharmony_ci dev_dbg(&pv->i2c->dev, 1448c2ecf20Sopenharmony_ci "Error reading ADC, try %d...\n", i); 1458c2ecf20Sopenharmony_ci if (i < 9) 1468c2ecf20Sopenharmony_ci msleep(10); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci mutex_unlock(&pv->lock); 1498c2ecf20Sopenharmony_ci return -1; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void wf_ad7417_release(struct kref *ref) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv = container_of(ref, 1558c2ecf20Sopenharmony_ci struct wf_ad7417_priv, ref); 1568c2ecf20Sopenharmony_ci kfree(pv); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void wf_ad7417_sensor_release(struct wf_sensor *sr) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv = sr->priv; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci kfree(sr->name); 1648c2ecf20Sopenharmony_ci kref_put(&pv->ref, wf_ad7417_release); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops wf_ad7417_temp_ops = { 1688c2ecf20Sopenharmony_ci .get_value = wf_ad7417_temp_get, 1698c2ecf20Sopenharmony_ci .release = wf_ad7417_sensor_release, 1708c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct wf_sensor_ops wf_ad7417_adc_ops = { 1748c2ecf20Sopenharmony_ci .get_value = wf_ad7417_adc_get, 1758c2ecf20Sopenharmony_ci .release = wf_ad7417_sensor_release, 1768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void wf_ad7417_add_sensor(struct wf_ad7417_priv *pv, 1808c2ecf20Sopenharmony_ci int index, const char *name, 1818c2ecf20Sopenharmony_ci const struct wf_sensor_ops *ops) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu); 1848c2ecf20Sopenharmony_ci pv->sensors[index].priv = pv; 1858c2ecf20Sopenharmony_ci pv->sensors[index].ops = ops; 1868c2ecf20Sopenharmony_ci if (!wf_register_sensor(&pv->sensors[index])) 1878c2ecf20Sopenharmony_ci kref_get(&pv->ref); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void wf_ad7417_init_chip(struct wf_ad7417_priv *pv) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int rc; 1938c2ecf20Sopenharmony_ci u8 buf[2]; 1948c2ecf20Sopenharmony_ci u8 config = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * Read ADC the configuration register and cache it. We 1988c2ecf20Sopenharmony_ci * also make sure Config2 contains proper values, I've seen 1998c2ecf20Sopenharmony_ci * cases where we got stale grabage in there, thus preventing 2008c2ecf20Sopenharmony_ci * proper reading of conv. values 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Clear Config2 */ 2048c2ecf20Sopenharmony_ci buf[0] = 5; 2058c2ecf20Sopenharmony_ci buf[1] = 0; 2068c2ecf20Sopenharmony_ci i2c_master_send(pv->i2c, buf, 2); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Read & cache Config1 */ 2098c2ecf20Sopenharmony_ci buf[0] = 1; 2108c2ecf20Sopenharmony_ci rc = i2c_master_send(pv->i2c, buf, 1); 2118c2ecf20Sopenharmony_ci if (rc > 0) { 2128c2ecf20Sopenharmony_ci rc = i2c_master_recv(pv->i2c, buf, 1); 2138c2ecf20Sopenharmony_ci if (rc > 0) { 2148c2ecf20Sopenharmony_ci config = buf[0]; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n", 2178c2ecf20Sopenharmony_ci config); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Disable shutdown mode */ 2208c2ecf20Sopenharmony_ci config &= 0xfe; 2218c2ecf20Sopenharmony_ci buf[0] = 1; 2228c2ecf20Sopenharmony_ci buf[1] = config; 2238c2ecf20Sopenharmony_ci rc = i2c_master_send(pv->i2c, buf, 2); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci if (rc <= 0) 2278c2ecf20Sopenharmony_ci dev_err(&pv->i2c->dev, "Error reading ADC config\n"); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pv->config = config; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int wf_ad7417_probe(struct i2c_client *client, 2338c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv; 2368c2ecf20Sopenharmony_ci const struct mpu_data *mpu; 2378c2ecf20Sopenharmony_ci const char *loc; 2388c2ecf20Sopenharmony_ci int cpu_nr; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); 2418c2ecf20Sopenharmony_ci if (!loc) { 2428c2ecf20Sopenharmony_ci dev_warn(&client->dev, "Missing hwsensor-location property!\n"); 2438c2ecf20Sopenharmony_ci return -ENXIO; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * Identify which CPU we belong to by looking at the first entry 2488c2ecf20Sopenharmony_ci * in the hwsensor-location list 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci if (!strncmp(loc, "CPU A", 5)) 2518c2ecf20Sopenharmony_ci cpu_nr = 0; 2528c2ecf20Sopenharmony_ci else if (!strncmp(loc, "CPU B", 5)) 2538c2ecf20Sopenharmony_ci cpu_nr = 1; 2548c2ecf20Sopenharmony_ci else { 2558c2ecf20Sopenharmony_ci pr_err("wf_ad7417: Can't identify location %s\n", loc); 2568c2ecf20Sopenharmony_ci return -ENXIO; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci mpu = wf_get_mpu(cpu_nr); 2598c2ecf20Sopenharmony_ci if (!mpu) { 2608c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to retrieve MPU data\n"); 2618c2ecf20Sopenharmony_ci return -ENXIO; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL); 2658c2ecf20Sopenharmony_ci if (pv == NULL) 2668c2ecf20Sopenharmony_ci return -ENODEV; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci kref_init(&pv->ref); 2698c2ecf20Sopenharmony_ci mutex_init(&pv->lock); 2708c2ecf20Sopenharmony_ci pv->i2c = client; 2718c2ecf20Sopenharmony_ci pv->cpu = cpu_nr; 2728c2ecf20Sopenharmony_ci pv->mpu = mpu; 2738c2ecf20Sopenharmony_ci dev_set_drvdata(&client->dev, pv); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Initialize the chip */ 2768c2ecf20Sopenharmony_ci wf_ad7417_init_chip(pv); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * We cannot rely on Apple device-tree giving us child 2808c2ecf20Sopenharmony_ci * node with the names of the individual sensors so we 2818c2ecf20Sopenharmony_ci * just hard code what we know about them 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops); 2848c2ecf20Sopenharmony_ci wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops); 2858c2ecf20Sopenharmony_ci wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops); 2868c2ecf20Sopenharmony_ci wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops); 2878c2ecf20Sopenharmony_ci wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int wf_ad7417_remove(struct i2c_client *client) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev); 2958c2ecf20Sopenharmony_ci int i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Mark client detached */ 2988c2ecf20Sopenharmony_ci pv->i2c = NULL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Release sensor */ 3018c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 3028c2ecf20Sopenharmony_ci wf_unregister_sensor(&pv->sensors[i]); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci kref_put(&pv->ref, wf_ad7417_release); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct i2c_device_id wf_ad7417_id[] = { 3108c2ecf20Sopenharmony_ci { "MAC,ad7417", 0 }, 3118c2ecf20Sopenharmony_ci { } 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wf_ad7417_id); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct of_device_id wf_ad7417_of_id[] = { 3168c2ecf20Sopenharmony_ci { .compatible = "ad7417", }, 3178c2ecf20Sopenharmony_ci { } 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wf_ad7417_of_id); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct i2c_driver wf_ad7417_driver = { 3228c2ecf20Sopenharmony_ci .driver = { 3238c2ecf20Sopenharmony_ci .name = "wf_ad7417", 3248c2ecf20Sopenharmony_ci .of_match_table = wf_ad7417_of_id, 3258c2ecf20Sopenharmony_ci }, 3268c2ecf20Sopenharmony_ci .probe = wf_ad7417_probe, 3278c2ecf20Sopenharmony_ci .remove = wf_ad7417_remove, 3288c2ecf20Sopenharmony_ci .id_table = wf_ad7417_id, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int wf_ad7417_init(void) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci /* This is only supported on these machines */ 3348c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("PowerMac7,2") && 3358c2ecf20Sopenharmony_ci !of_machine_is_compatible("PowerMac7,3") && 3368c2ecf20Sopenharmony_ci !of_machine_is_compatible("RackMac3,1")) 3378c2ecf20Sopenharmony_ci return -ENODEV; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return i2c_add_driver(&wf_ad7417_driver); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void wf_ad7417_exit(void) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci i2c_del_driver(&wf_ad7417_driver); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cimodule_init(wf_ad7417_init); 3488c2ecf20Sopenharmony_cimodule_exit(wf_ad7417_exit); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 3518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs"); 3528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3538c2ecf20Sopenharmony_ci 354