1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459 |
- /*
- * nct6683 - Driver for the hardware monitoring functionality of
- * Nuvoton NCT6683D eSIO
- *
- * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
- *
- * Derived from nct6775 driver
- * Copyright (C) 2012, 2013 Guenter Roeck <linux@roeck-us.net>
- *
- * 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 2 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.
- *
- * Supports the following chips:
- *
- * Chip #vin #fan #pwm #temp chip ID
- * nct6683d 21(1) 16 8 32(1) 0xc730
- *
- * Notes:
- * (1) Total number of vin and temp inputs is 32.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/acpi.h>
- #include <linux/dmi.h>
- #include <linux/err.h>
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/jiffies.h>
- #include <linux/hwmon.h>
- #include <linux/hwmon-sysfs.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- enum kinds { nct6683 };
- static bool force;
- module_param(force, bool, 0);
- MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards");
- static const char * const nct6683_device_names[] = {
- "nct6683",
- };
- static const char * const nct6683_chip_names[] = {
- "NCT6683D",
- };
- #define DRVNAME "nct6683"
- /*
- * Super-I/O constants and functions
- */
- #define NCT6683_LD_ACPI 0x0a
- #define NCT6683_LD_HWM 0x0b
- #define NCT6683_LD_VID 0x0d
- #define SIO_REG_LDSEL 0x07 /* Logical device select */
- #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
- #define SIO_REG_ENABLE 0x30 /* Logical device enable */
- #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
- #define SIO_NCT6681_ID 0xb270 /* for later */
- #define SIO_NCT6683_ID 0xc730
- #define SIO_ID_MASK 0xFFF0
- static inline void
- superio_outb(int ioreg, int reg, int val)
- {
- outb(reg, ioreg);
- outb(val, ioreg + 1);
- }
- static inline int
- superio_inb(int ioreg, int reg)
- {
- outb(reg, ioreg);
- return inb(ioreg + 1);
- }
- static inline void
- superio_select(int ioreg, int ld)
- {
- outb(SIO_REG_LDSEL, ioreg);
- outb(ld, ioreg + 1);
- }
- static inline int
- superio_enter(int ioreg)
- {
- /*
- * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
- */
- if (!request_muxed_region(ioreg, 2, DRVNAME))
- return -EBUSY;
- outb(0x87, ioreg);
- outb(0x87, ioreg);
- return 0;
- }
- static inline void
- superio_exit(int ioreg)
- {
- outb(0xaa, ioreg);
- outb(0x02, ioreg);
- outb(0x02, ioreg + 1);
- release_region(ioreg, 2);
- }
- /*
- * ISA constants
- */
- #define IOREGION_ALIGNMENT (~7)
- #define IOREGION_OFFSET 4 /* Use EC port 1 */
- #define IOREGION_LENGTH 4
- #define EC_PAGE_REG 0
- #define EC_INDEX_REG 1
- #define EC_DATA_REG 2
- #define EC_EVENT_REG 3
- /* Common and NCT6683 specific data */
- #define NCT6683_NUM_REG_MON 32
- #define NCT6683_NUM_REG_FAN 16
- #define NCT6683_NUM_REG_PWM 8
- #define NCT6683_REG_MON(x) (0x100 + (x) * 2)
- #define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2)
- #define NCT6683_REG_PWM(x) (0x160 + (x))
- #define NCT6683_REG_MON_STS(x) (0x174 + (x))
- #define NCT6683_REG_IDLE(x) (0x178 + (x))
- #define NCT6683_REG_FAN_STS(x) (0x17c + (x))
- #define NCT6683_REG_FAN_ERRSTS 0x17e
- #define NCT6683_REG_FAN_INITSTS 0x17f
- #define NCT6683_HWM_CFG 0x180
- #define NCT6683_REG_MON_CFG(x) (0x1a0 + (x))
- #define NCT6683_REG_FANIN_CFG(x) (0x1c0 + (x))
- #define NCT6683_REG_FANOUT_CFG(x) (0x1d0 + (x))
- #define NCT6683_REG_INTEL_TEMP_MAX(x) (0x901 + (x) * 16)
- #define NCT6683_REG_INTEL_TEMP_CRIT(x) (0x90d + (x) * 16)
- #define NCT6683_REG_TEMP_HYST(x) (0x330 + (x)) /* 8 bit */
- #define NCT6683_REG_TEMP_MAX(x) (0x350 + (x)) /* 8 bit */
- #define NCT6683_REG_MON_HIGH(x) (0x370 + (x) * 2) /* 8 bit */
- #define NCT6683_REG_MON_LOW(x) (0x371 + (x) * 2) /* 8 bit */
- #define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */
- #define NCT6683_REG_CUSTOMER_ID 0x602
- #define NCT6683_CUSTOMER_ID_INTEL 0x805
- #define NCT6683_REG_BUILD_YEAR 0x604
- #define NCT6683_REG_BUILD_MONTH 0x605
- #define NCT6683_REG_BUILD_DAY 0x606
- #define NCT6683_REG_SERIAL 0x607
- #define NCT6683_REG_VERSION_HI 0x608
- #define NCT6683_REG_VERSION_LO 0x609
- #define NCT6683_REG_CR_CASEOPEN 0xe8
- #define NCT6683_CR_CASEOPEN_MASK (1 << 7)
- #define NCT6683_REG_CR_BEEP 0xe0
- #define NCT6683_CR_BEEP_MASK (1 << 6)
- static const char *const nct6683_mon_label[] = {
- NULL, /* disabled */
- "Local",
- "Diode 0 (curr)",
- "Diode 1 (curr)",
- "Diode 2 (curr)",
- "Diode 0 (volt)",
- "Diode 1 (volt)",
- "Diode 2 (volt)",
- "Thermistor 14",
- "Thermistor 15",
- "Thermistor 16",
- "Thermistor 0",
- "Thermistor 1",
- "Thermistor 2",
- "Thermistor 3",
- "Thermistor 4",
- "Thermistor 5", /* 0x10 */
- "Thermistor 6",
- "Thermistor 7",
- "Thermistor 8",
- "Thermistor 9",
- "Thermistor 10",
- "Thermistor 11",
- "Thermistor 12",
- "Thermistor 13",
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- "PECI 0.0", /* 0x20 */
- "PECI 1.0",
- "PECI 2.0",
- "PECI 3.0",
- "PECI 0.1",
- "PECI 1.1",
- "PECI 2.1",
- "PECI 3.1",
- "PECI DIMM 0",
- "PECI DIMM 1",
- "PECI DIMM 2",
- "PECI DIMM 3",
- NULL, NULL, NULL, NULL,
- "PCH CPU", /* 0x30 */
- "PCH CHIP",
- "PCH CHIP CPU MAX",
- "PCH MCH",
- "PCH DIMM 0",
- "PCH DIMM 1",
- "PCH DIMM 2",
- "PCH DIMM 3",
- "SMBus 0",
- "SMBus 1",
- "SMBus 2",
- "SMBus 3",
- "SMBus 4",
- "SMBus 5",
- "DIMM 0",
- "DIMM 1",
- "DIMM 2", /* 0x40 */
- "DIMM 3",
- "AMD TSI Addr 90h",
- "AMD TSI Addr 92h",
- "AMD TSI Addr 94h",
- "AMD TSI Addr 96h",
- "AMD TSI Addr 98h",
- "AMD TSI Addr 9ah",
- "AMD TSI Addr 9ch",
- "AMD TSI Addr 9dh",
- NULL, NULL, NULL, NULL, NULL, NULL,
- "Virtual 0", /* 0x50 */
- "Virtual 1",
- "Virtual 2",
- "Virtual 3",
- "Virtual 4",
- "Virtual 5",
- "Virtual 6",
- "Virtual 7",
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- "VCC", /* 0x60 voltage sensors */
- "VSB",
- "AVSB",
- "VTT",
- "VBAT",
- "VREF",
- "VIN0",
- "VIN1",
- "VIN2",
- "VIN3",
- "VIN4",
- "VIN5",
- "VIN6",
- "VIN7",
- "VIN8",
- "VIN9",
- "VIN10",
- "VIN11",
- "VIN12",
- "VIN13",
- "VIN14",
- "VIN15",
- "VIN16",
- };
- #define NUM_MON_LABELS ARRAY_SIZE(nct6683_mon_label)
- #define MON_VOLTAGE_START 0x60
- /* ------------------------------------------------------- */
- struct nct6683_data {
- int addr; /* IO base of EC space */
- int sioreg; /* SIO register */
- enum kinds kind;
- u16 customer_id;
- struct device *hwmon_dev;
- const struct attribute_group *groups[6];
- int temp_num; /* number of temperature attributes */
- u8 temp_index[NCT6683_NUM_REG_MON];
- u8 temp_src[NCT6683_NUM_REG_MON];
- u8 in_num; /* number of voltage attributes */
- u8 in_index[NCT6683_NUM_REG_MON];
- u8 in_src[NCT6683_NUM_REG_MON];
- struct mutex update_lock; /* used to protect sensor updates */
- bool valid; /* true if following fields are valid */
- unsigned long last_updated; /* In jiffies */
- /* Voltage attribute values */
- u8 in[3][NCT6683_NUM_REG_MON]; /* [0]=in, [1]=in_max, [2]=in_min */
- /* Temperature attribute values */
- s16 temp_in[NCT6683_NUM_REG_MON];
- s8 temp[4][NCT6683_NUM_REG_MON];/* [0]=min, [1]=max, [2]=hyst,
- * [3]=crit
- */
- /* Fan attribute values */
- unsigned int rpm[NCT6683_NUM_REG_FAN];
- u16 fan_min[NCT6683_NUM_REG_FAN];
- u8 fanin_cfg[NCT6683_NUM_REG_FAN];
- u8 fanout_cfg[NCT6683_NUM_REG_FAN];
- u16 have_fan; /* some fan inputs can be disabled */
- u8 have_pwm;
- u8 pwm[NCT6683_NUM_REG_PWM];
- #ifdef CONFIG_PM
- /* Remember extra register values over suspend/resume */
- u8 hwm_cfg;
- #endif
- };
- struct nct6683_sio_data {
- int sioreg;
- enum kinds kind;
- };
- struct sensor_device_template {
- struct device_attribute dev_attr;
- union {
- struct {
- u8 nr;
- u8 index;
- } s;
- int index;
- } u;
- bool s2; /* true if both index and nr are used */
- };
- struct sensor_device_attr_u {
- union {
- struct sensor_device_attribute a1;
- struct sensor_device_attribute_2 a2;
- } u;
- char name[32];
- };
- #define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \
- .attr = {.name = _template, .mode = _mode }, \
- .show = _show, \
- .store = _store, \
- }
- #define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \
- { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
- .u.index = _index, \
- .s2 = false }
- #define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
- _nr, _index) \
- { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
- .u.s.index = _index, \
- .u.s.nr = _nr, \
- .s2 = true }
- #define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \
- static struct sensor_device_template sensor_dev_template_##_name \
- = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \
- _index)
- #define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \
- _nr, _index) \
- static struct sensor_device_template sensor_dev_template_##_name \
- = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
- _nr, _index)
- struct sensor_template_group {
- struct sensor_device_template **templates;
- umode_t (*is_visible)(struct kobject *, struct attribute *, int);
- int base;
- };
- static struct attribute_group *
- nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg,
- int repeat)
- {
- struct sensor_device_attribute_2 *a2;
- struct sensor_device_attribute *a;
- struct sensor_device_template **t;
- struct sensor_device_attr_u *su;
- struct attribute_group *group;
- struct attribute **attrs;
- int i, j, count;
- if (repeat <= 0)
- return ERR_PTR(-EINVAL);
- t = tg->templates;
- for (count = 0; *t; t++, count++)
- ;
- if (count == 0)
- return ERR_PTR(-EINVAL);
- group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
- if (group == NULL)
- return ERR_PTR(-ENOMEM);
- attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1),
- GFP_KERNEL);
- if (attrs == NULL)
- return ERR_PTR(-ENOMEM);
- su = devm_kzalloc(dev, sizeof(*su) * repeat * count,
- GFP_KERNEL);
- if (su == NULL)
- return ERR_PTR(-ENOMEM);
- group->attrs = attrs;
- group->is_visible = tg->is_visible;
- for (i = 0; i < repeat; i++) {
- t = tg->templates;
- for (j = 0; *t != NULL; j++) {
- snprintf(su->name, sizeof(su->name),
- (*t)->dev_attr.attr.name, tg->base + i);
- if ((*t)->s2) {
- a2 = &su->u.a2;
- sysfs_attr_init(&a2->dev_attr.attr);
- a2->dev_attr.attr.name = su->name;
- a2->nr = (*t)->u.s.nr + i;
- a2->index = (*t)->u.s.index;
- a2->dev_attr.attr.mode =
- (*t)->dev_attr.attr.mode;
- a2->dev_attr.show = (*t)->dev_attr.show;
- a2->dev_attr.store = (*t)->dev_attr.store;
- *attrs = &a2->dev_attr.attr;
- } else {
- a = &su->u.a1;
- sysfs_attr_init(&a->dev_attr.attr);
- a->dev_attr.attr.name = su->name;
- a->index = (*t)->u.index + i;
- a->dev_attr.attr.mode =
- (*t)->dev_attr.attr.mode;
- a->dev_attr.show = (*t)->dev_attr.show;
- a->dev_attr.store = (*t)->dev_attr.store;
- *attrs = &a->dev_attr.attr;
- }
- attrs++;
- su++;
- t++;
- }
- }
- return group;
- }
- /* LSB is 16 mV, except for the following sources, where it is 32 mV */
- #define MON_SRC_VCC 0x60
- #define MON_SRC_VSB 0x61
- #define MON_SRC_AVSB 0x62
- #define MON_SRC_VBAT 0x64
- static inline long in_from_reg(u16 reg, u8 src)
- {
- int scale = 16;
- if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB ||
- src == MON_SRC_VBAT)
- scale <<= 1;
- return reg * scale;
- }
- static inline u16 in_to_reg(u32 val, u8 src)
- {
- int scale = 16;
- if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB ||
- src == MON_SRC_VBAT)
- scale <<= 1;
- return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127);
- }
- static u16 nct6683_read(struct nct6683_data *data, u16 reg)
- {
- int res;
- outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */
- outb_p(reg >> 8, data->addr + EC_PAGE_REG);
- outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
- res = inb_p(data->addr + EC_DATA_REG);
- return res;
- }
- static u16 nct6683_read16(struct nct6683_data *data, u16 reg)
- {
- return (nct6683_read(data, reg) << 8) | nct6683_read(data, reg + 1);
- }
- static void nct6683_write(struct nct6683_data *data, u16 reg, u16 value)
- {
- outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */
- outb_p(reg >> 8, data->addr + EC_PAGE_REG);
- outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
- outb_p(value & 0xff, data->addr + EC_DATA_REG);
- }
- static int get_in_reg(struct nct6683_data *data, int nr, int index)
- {
- int ch = data->in_index[index];
- int reg = -EINVAL;
- switch (nr) {
- case 0:
- reg = NCT6683_REG_MON(ch);
- break;
- case 1:
- if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
- reg = NCT6683_REG_MON_LOW(ch);
- break;
- case 2:
- if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
- reg = NCT6683_REG_MON_HIGH(ch);
- break;
- default:
- break;
- }
- return reg;
- }
- static int get_temp_reg(struct nct6683_data *data, int nr, int index)
- {
- int ch = data->temp_index[index];
- int reg = -EINVAL;
- switch (data->customer_id) {
- case NCT6683_CUSTOMER_ID_INTEL:
- switch (nr) {
- default:
- case 1: /* max */
- reg = NCT6683_REG_INTEL_TEMP_MAX(ch);
- break;
- case 3: /* crit */
- reg = NCT6683_REG_INTEL_TEMP_CRIT(ch);
- break;
- }
- break;
- default:
- switch (nr) {
- default:
- case 0: /* min */
- reg = NCT6683_REG_MON_LOW(ch);
- break;
- case 1: /* max */
- reg = NCT6683_REG_TEMP_MAX(ch);
- break;
- case 2: /* hyst */
- reg = NCT6683_REG_TEMP_HYST(ch);
- break;
- case 3: /* crit */
- reg = NCT6683_REG_MON_HIGH(ch);
- break;
- }
- break;
- }
- return reg;
- }
- static void nct6683_update_pwm(struct device *dev)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- int i;
- for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
- if (!(data->have_pwm & (1 << i)))
- continue;
- data->pwm[i] = nct6683_read(data, NCT6683_REG_PWM(i));
- }
- }
- static struct nct6683_data *nct6683_update_device(struct device *dev)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- int i, j;
- mutex_lock(&data->update_lock);
- if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- /* Measured voltages and limits */
- for (i = 0; i < data->in_num; i++) {
- for (j = 0; j < 3; j++) {
- int reg = get_in_reg(data, j, i);
- if (reg >= 0)
- data->in[j][i] =
- nct6683_read(data, reg);
- }
- }
- /* Measured temperatures and limits */
- for (i = 0; i < data->temp_num; i++) {
- u8 ch = data->temp_index[i];
- data->temp_in[i] = nct6683_read16(data,
- NCT6683_REG_MON(ch));
- for (j = 0; j < 4; j++) {
- int reg = get_temp_reg(data, j, i);
- if (reg >= 0)
- data->temp[j][i] =
- nct6683_read(data, reg);
- }
- }
- /* Measured fan speeds and limits */
- for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
- if (!(data->have_fan & (1 << i)))
- continue;
- data->rpm[i] = nct6683_read16(data,
- NCT6683_REG_FAN_RPM(i));
- data->fan_min[i] = nct6683_read16(data,
- NCT6683_REG_FAN_MIN(i));
- }
- nct6683_update_pwm(dev);
- data->last_updated = jiffies;
- data->valid = true;
- }
- mutex_unlock(&data->update_lock);
- return data;
- }
- /*
- * Sysfs callback functions
- */
- static ssize_t
- show_in_label(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int nr = sattr->index;
- return sprintf(buf, "%s\n", nct6683_mon_label[data->in_src[nr]]);
- }
- static ssize_t
- show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int index = sattr->index;
- int nr = sattr->nr;
- return sprintf(buf, "%ld\n",
- in_from_reg(data->in[index][nr], data->in_index[index]));
- }
- static umode_t nct6683_in_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
- {
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct6683_data *data = dev_get_drvdata(dev);
- int nr = index % 4; /* attribute */
- /*
- * Voltage limits exist for Intel boards,
- * but register location and encoding is unknown
- */
- if ((nr == 2 || nr == 3) &&
- data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
- return 0;
- return attr->mode;
- }
- SENSOR_TEMPLATE(in_label, "in%d_label", S_IRUGO, show_in_label, NULL, 0);
- SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
- SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IRUGO, show_in_reg, NULL, 0, 1);
- SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IRUGO, show_in_reg, NULL, 0, 2);
- static struct sensor_device_template *nct6683_attributes_in_template[] = {
- &sensor_dev_template_in_label,
- &sensor_dev_template_in_input,
- &sensor_dev_template_in_min,
- &sensor_dev_template_in_max,
- NULL
- };
- static struct sensor_template_group nct6683_in_template_group = {
- .templates = nct6683_attributes_in_template,
- .is_visible = nct6683_in_is_visible,
- };
- static ssize_t
- show_fan(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- return sprintf(buf, "%d\n", data->rpm[sattr->index]);
- }
- static ssize_t
- show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct nct6683_data *data = nct6683_update_device(dev);
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int nr = sattr->index;
- return sprintf(buf, "%d\n", data->fan_min[nr]);
- }
- static ssize_t
- show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- return sprintf(buf, "%d\n",
- ((data->fanin_cfg[sattr->index] >> 5) & 0x03) + 1);
- }
- static umode_t nct6683_fan_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
- {
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct6683_data *data = dev_get_drvdata(dev);
- int fan = index / 3; /* fan index */
- int nr = index % 3; /* attribute index */
- if (!(data->have_fan & (1 << fan)))
- return 0;
- /*
- * Intel may have minimum fan speed limits,
- * but register location and encoding are unknown.
- */
- if (nr == 2 && data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
- return 0;
- return attr->mode;
- }
- SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
- SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IRUGO, show_fan_pulses, NULL, 0);
- SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IRUGO, show_fan_min, NULL, 0);
- /*
- * nct6683_fan_is_visible uses the index into the following array
- * to determine if attributes should be created or not.
- * Any change in order or content must be matched.
- */
- static struct sensor_device_template *nct6683_attributes_fan_template[] = {
- &sensor_dev_template_fan_input,
- &sensor_dev_template_fan_pulses,
- &sensor_dev_template_fan_min,
- NULL
- };
- static struct sensor_template_group nct6683_fan_template_group = {
- .templates = nct6683_attributes_fan_template,
- .is_visible = nct6683_fan_is_visible,
- .base = 1,
- };
- static ssize_t
- show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int nr = sattr->index;
- return sprintf(buf, "%s\n", nct6683_mon_label[data->temp_src[nr]]);
- }
- static ssize_t
- show_temp8(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int index = sattr->index;
- int nr = sattr->nr;
- return sprintf(buf, "%d\n", data->temp[index][nr] * 1000);
- }
- static ssize_t
- show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int nr = sattr->index;
- int temp = data->temp[1][nr] - data->temp[2][nr];
- return sprintf(buf, "%d\n", temp * 1000);
- }
- static ssize_t
- show_temp16(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- struct nct6683_data *data = nct6683_update_device(dev);
- int index = sattr->index;
- return sprintf(buf, "%d\n", (data->temp_in[index] / 128) * 500);
- }
- /*
- * Temperature sensor type is determined by temperature source
- * and can not be modified.
- * 0x02..0x07: Thermal diode
- * 0x08..0x18: Thermistor
- * 0x20..0x2b: Intel PECI
- * 0x42..0x49: AMD TSI
- * Others are unspecified (not visible)
- */
- static int get_temp_type(u8 src)
- {
- if (src >= 0x02 && src <= 0x07)
- return 3; /* thermal diode */
- else if (src >= 0x08 && src <= 0x18)
- return 4; /* thermistor */
- else if (src >= 0x20 && src <= 0x2b)
- return 6; /* PECI */
- else if (src >= 0x42 && src <= 0x49)
- return 5;
- return 0;
- }
- static ssize_t
- show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct nct6683_data *data = nct6683_update_device(dev);
- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int nr = sattr->index;
- return sprintf(buf, "%d\n", get_temp_type(data->temp_src[nr]));
- }
- static umode_t nct6683_temp_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
- {
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct6683_data *data = dev_get_drvdata(dev);
- int temp = index / 7; /* temp index */
- int nr = index % 7; /* attribute index */
- /*
- * Intel does not have low temperature limits or temperature hysteresis
- * registers, or at least register location and encoding is unknown.
- */
- if ((nr == 2 || nr == 4) &&
- data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
- return 0;
- if (nr == 6 && get_temp_type(data->temp_src[temp]) == 0)
- return 0; /* type */
- return attr->mode;
- }
- SENSOR_TEMPLATE(temp_input, "temp%d_input", S_IRUGO, show_temp16, NULL, 0);
- SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
- SENSOR_TEMPLATE_2(temp_min, "temp%d_min", S_IRUGO, show_temp8, NULL, 0, 0);
- SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO, show_temp8, NULL, 0, 1);
- SENSOR_TEMPLATE(temp_max_hyst, "temp%d_max_hyst", S_IRUGO, show_temp_hyst, NULL,
- 0);
- SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO, show_temp8, NULL, 0, 3);
- SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO, show_temp_type, NULL, 0);
- /*
- * nct6683_temp_is_visible uses the index into the following array
- * to determine if attributes should be created or not.
- * Any change in order or content must be matched.
- */
- static struct sensor_device_template *nct6683_attributes_temp_template[] = {
- &sensor_dev_template_temp_input,
- &sensor_dev_template_temp_label,
- &sensor_dev_template_temp_min, /* 2 */
- &sensor_dev_template_temp_max, /* 3 */
- &sensor_dev_template_temp_max_hyst, /* 4 */
- &sensor_dev_template_temp_crit, /* 5 */
- &sensor_dev_template_temp_type, /* 6 */
- NULL
- };
- static struct sensor_template_group nct6683_temp_template_group = {
- .templates = nct6683_attributes_temp_template,
- .is_visible = nct6683_temp_is_visible,
- .base = 1,
- };
- static ssize_t
- show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct nct6683_data *data = nct6683_update_device(dev);
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- int index = sattr->index;
- return sprintf(buf, "%d\n", data->pwm[index]);
- }
- SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0);
- static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
- {
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct6683_data *data = dev_get_drvdata(dev);
- int pwm = index; /* pwm index */
- if (!(data->have_pwm & (1 << pwm)))
- return 0;
- return attr->mode;
- }
- static struct sensor_device_template *nct6683_attributes_pwm_template[] = {
- &sensor_dev_template_pwm,
- NULL
- };
- static struct sensor_template_group nct6683_pwm_template_group = {
- .templates = nct6683_attributes_pwm_template,
- .is_visible = nct6683_pwm_is_visible,
- .base = 1,
- };
- static ssize_t
- show_global_beep(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- int ret;
- u8 reg;
- mutex_lock(&data->update_lock);
- ret = superio_enter(data->sioreg);
- if (ret)
- goto error;
- superio_select(data->sioreg, NCT6683_LD_HWM);
- reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
- superio_exit(data->sioreg);
- mutex_unlock(&data->update_lock);
- return sprintf(buf, "%u\n", !!(reg & NCT6683_CR_BEEP_MASK));
- error:
- mutex_unlock(&data->update_lock);
- return ret;
- }
- static ssize_t
- store_global_beep(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- unsigned long val;
- u8 reg;
- int ret;
- if (kstrtoul(buf, 10, &val) || (val != 0 && val != 1))
- return -EINVAL;
- mutex_lock(&data->update_lock);
- ret = superio_enter(data->sioreg);
- if (ret) {
- count = ret;
- goto error;
- }
- superio_select(data->sioreg, NCT6683_LD_HWM);
- reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
- if (val)
- reg |= NCT6683_CR_BEEP_MASK;
- else
- reg &= ~NCT6683_CR_BEEP_MASK;
- superio_outb(data->sioreg, NCT6683_REG_CR_BEEP, reg);
- superio_exit(data->sioreg);
- error:
- mutex_unlock(&data->update_lock);
- return count;
- }
- /* Case open detection */
- static ssize_t
- show_caseopen(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- int ret;
- u8 reg;
- mutex_lock(&data->update_lock);
- ret = superio_enter(data->sioreg);
- if (ret)
- goto error;
- superio_select(data->sioreg, NCT6683_LD_ACPI);
- reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
- superio_exit(data->sioreg);
- mutex_unlock(&data->update_lock);
- return sprintf(buf, "%u\n", !(reg & NCT6683_CR_CASEOPEN_MASK));
- error:
- mutex_unlock(&data->update_lock);
- return ret;
- }
- static ssize_t
- clear_caseopen(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- unsigned long val;
- u8 reg;
- int ret;
- if (kstrtoul(buf, 10, &val) || val != 0)
- return -EINVAL;
- mutex_lock(&data->update_lock);
- /*
- * Use CR registers to clear caseopen status.
- * Caseopen is activ low, clear by writing 1 into the register.
- */
- ret = superio_enter(data->sioreg);
- if (ret) {
- count = ret;
- goto error;
- }
- superio_select(data->sioreg, NCT6683_LD_ACPI);
- reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
- reg |= NCT6683_CR_CASEOPEN_MASK;
- superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
- reg &= ~NCT6683_CR_CASEOPEN_MASK;
- superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
- superio_exit(data->sioreg);
- data->valid = false; /* Force cache refresh */
- error:
- mutex_unlock(&data->update_lock);
- return count;
- }
- static DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen,
- clear_caseopen);
- static DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_global_beep,
- store_global_beep);
- static struct attribute *nct6683_attributes_other[] = {
- &dev_attr_intrusion0_alarm.attr,
- &dev_attr_beep_enable.attr,
- NULL
- };
- static const struct attribute_group nct6683_group_other = {
- .attrs = nct6683_attributes_other,
- };
- /* Get the monitoring functions started */
- static inline void nct6683_init_device(struct nct6683_data *data)
- {
- u8 tmp;
- /* Start hardware monitoring if needed */
- tmp = nct6683_read(data, NCT6683_HWM_CFG);
- if (!(tmp & 0x80))
- nct6683_write(data, NCT6683_HWM_CFG, tmp | 0x80);
- }
- /*
- * There are a total of 24 fan inputs. Each can be configured as input
- * or as output. A maximum of 16 inputs and 8 outputs is configurable.
- */
- static void
- nct6683_setup_fans(struct nct6683_data *data)
- {
- int i;
- u8 reg;
- for (i = 0; i < NCT6683_NUM_REG_FAN; i++) {
- reg = nct6683_read(data, NCT6683_REG_FANIN_CFG(i));
- if (reg & 0x80)
- data->have_fan |= 1 << i;
- data->fanin_cfg[i] = reg;
- }
- for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
- reg = nct6683_read(data, NCT6683_REG_FANOUT_CFG(i));
- if (reg & 0x80)
- data->have_pwm |= 1 << i;
- data->fanout_cfg[i] = reg;
- }
- }
- /*
- * Translation from monitoring register to temperature and voltage attributes
- * ==========================================================================
- *
- * There are a total of 32 monitoring registers. Each can be assigned to either
- * a temperature or voltage monitoring source.
- * NCT6683_REG_MON_CFG(x) defines assignment for each monitoring source.
- *
- * Temperature and voltage attribute mapping is determined by walking through
- * the NCT6683_REG_MON_CFG registers. If the assigned source is
- * a temperature, temp_index[n] is set to the monitor register index, and
- * temp_src[n] is set to the temperature source. If the assigned source is
- * a voltage, the respective values are stored in in_index[] and in_src[],
- * respectively.
- */
- static void nct6683_setup_sensors(struct nct6683_data *data)
- {
- u8 reg;
- int i;
- data->temp_num = 0;
- data->in_num = 0;
- for (i = 0; i < NCT6683_NUM_REG_MON; i++) {
- reg = nct6683_read(data, NCT6683_REG_MON_CFG(i)) & 0x7f;
- /* Ignore invalid assignments */
- if (reg >= NUM_MON_LABELS)
- continue;
- /* Skip if disabled or reserved */
- if (nct6683_mon_label[reg] == NULL)
- continue;
- if (reg < MON_VOLTAGE_START) {
- data->temp_index[data->temp_num] = i;
- data->temp_src[data->temp_num] = reg;
- data->temp_num++;
- } else {
- data->in_index[data->in_num] = i;
- data->in_src[data->in_num] = reg;
- data->in_num++;
- }
- }
- }
- static int nct6683_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct nct6683_sio_data *sio_data = dev->platform_data;
- struct attribute_group *group;
- struct nct6683_data *data;
- struct device *hwmon_dev;
- struct resource *res;
- int groups = 0;
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
- return -EBUSY;
- data = devm_kzalloc(dev, sizeof(struct nct6683_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->kind = sio_data->kind;
- data->sioreg = sio_data->sioreg;
- data->addr = res->start;
- mutex_init(&data->update_lock);
- platform_set_drvdata(pdev, data);
- data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID);
- nct6683_init_device(data);
- nct6683_setup_fans(data);
- nct6683_setup_sensors(data);
- /* Register sysfs hooks */
- if (data->have_pwm) {
- group = nct6683_create_attr_group(dev,
- &nct6683_pwm_template_group,
- fls(data->have_pwm));
- if (IS_ERR(group))
- return PTR_ERR(group);
- data->groups[groups++] = group;
- }
- if (data->in_num) {
- group = nct6683_create_attr_group(dev,
- &nct6683_in_template_group,
- data->in_num);
- if (IS_ERR(group))
- return PTR_ERR(group);
- data->groups[groups++] = group;
- }
- if (data->have_fan) {
- group = nct6683_create_attr_group(dev,
- &nct6683_fan_template_group,
- fls(data->have_fan));
- if (IS_ERR(group))
- return PTR_ERR(group);
- data->groups[groups++] = group;
- }
- if (data->temp_num) {
- group = nct6683_create_attr_group(dev,
- &nct6683_temp_template_group,
- data->temp_num);
- if (IS_ERR(group))
- return PTR_ERR(group);
- data->groups[groups++] = group;
- }
- data->groups[groups++] = &nct6683_group_other;
- dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n",
- nct6683_chip_names[data->kind],
- nct6683_read(data, NCT6683_REG_VERSION_HI),
- nct6683_read(data, NCT6683_REG_VERSION_LO),
- nct6683_read(data, NCT6683_REG_BUILD_MONTH),
- nct6683_read(data, NCT6683_REG_BUILD_DAY),
- nct6683_read(data, NCT6683_REG_BUILD_YEAR));
- hwmon_dev = devm_hwmon_device_register_with_groups(dev,
- nct6683_device_names[data->kind], data, data->groups);
- return PTR_ERR_OR_ZERO(hwmon_dev);
- }
- #ifdef CONFIG_PM
- static int nct6683_suspend(struct device *dev)
- {
- struct nct6683_data *data = nct6683_update_device(dev);
- mutex_lock(&data->update_lock);
- data->hwm_cfg = nct6683_read(data, NCT6683_HWM_CFG);
- mutex_unlock(&data->update_lock);
- return 0;
- }
- static int nct6683_resume(struct device *dev)
- {
- struct nct6683_data *data = dev_get_drvdata(dev);
- mutex_lock(&data->update_lock);
- nct6683_write(data, NCT6683_HWM_CFG, data->hwm_cfg);
- /* Force re-reading all values */
- data->valid = false;
- mutex_unlock(&data->update_lock);
- return 0;
- }
- static const struct dev_pm_ops nct6683_dev_pm_ops = {
- .suspend = nct6683_suspend,
- .resume = nct6683_resume,
- .freeze = nct6683_suspend,
- .restore = nct6683_resume,
- };
- #define NCT6683_DEV_PM_OPS (&nct6683_dev_pm_ops)
- #else
- #define NCT6683_DEV_PM_OPS NULL
- #endif /* CONFIG_PM */
- static struct platform_driver nct6683_driver = {
- .driver = {
- .name = DRVNAME,
- .pm = NCT6683_DEV_PM_OPS,
- },
- .probe = nct6683_probe,
- };
- static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
- {
- const char *board_vendor;
- int addr;
- u16 val;
- int err;
- /*
- * Only run on Intel boards unless the 'force' module parameter is set
- */
- if (!force) {
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- if (!board_vendor || strcmp(board_vendor, "Intel Corporation"))
- return -ENODEV;
- }
- err = superio_enter(sioaddr);
- if (err)
- return err;
- val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
- | superio_inb(sioaddr, SIO_REG_DEVID + 1);
- switch (val & SIO_ID_MASK) {
- case SIO_NCT6683_ID:
- sio_data->kind = nct6683;
- break;
- default:
- if (val != 0xffff)
- pr_debug("unsupported chip ID: 0x%04x\n", val);
- goto fail;
- }
- /* We have a known chip, find the HWM I/O address */
- superio_select(sioaddr, NCT6683_LD_HWM);
- val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8)
- | superio_inb(sioaddr, SIO_REG_ADDR + 1);
- addr = val & IOREGION_ALIGNMENT;
- if (addr == 0) {
- pr_err("EC base I/O port unconfigured\n");
- goto fail;
- }
- /* Activate logical device if needed */
- val = superio_inb(sioaddr, SIO_REG_ENABLE);
- if (!(val & 0x01)) {
- pr_err("EC is disabled\n");
- goto fail;
- }
- superio_exit(sioaddr);
- pr_info("Found %s or compatible chip at %#x:%#x\n",
- nct6683_chip_names[sio_data->kind], sioaddr, addr);
- sio_data->sioreg = sioaddr;
- return addr;
- fail:
- superio_exit(sioaddr);
- return -ENODEV;
- }
- /*
- * when Super-I/O functions move to a separate file, the Super-I/O
- * bus will manage the lifetime of the device and this module will only keep
- * track of the nct6683 driver. But since we use platform_device_alloc(), we
- * must keep track of the device
- */
- static struct platform_device *pdev[2];
- static int __init sensors_nct6683_init(void)
- {
- struct nct6683_sio_data sio_data;
- int sioaddr[2] = { 0x2e, 0x4e };
- struct resource res;
- bool found = false;
- int address;
- int i, err;
- err = platform_driver_register(&nct6683_driver);
- if (err)
- return err;
- /*
- * initialize sio_data->kind and sio_data->sioreg.
- *
- * when Super-I/O functions move to a separate file, the Super-I/O
- * driver will probe 0x2e and 0x4e and auto-detect the presence of a
- * nct6683 hardware monitor, and call probe()
- */
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- address = nct6683_find(sioaddr[i], &sio_data);
- if (address <= 0)
- continue;
- found = true;
- pdev[i] = platform_device_alloc(DRVNAME, address);
- if (!pdev[i]) {
- err = -ENOMEM;
- goto exit_device_unregister;
- }
- err = platform_device_add_data(pdev[i], &sio_data,
- sizeof(struct nct6683_sio_data));
- if (err)
- goto exit_device_put;
- memset(&res, 0, sizeof(res));
- res.name = DRVNAME;
- res.start = address + IOREGION_OFFSET;
- res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
- res.flags = IORESOURCE_IO;
- err = acpi_check_resource_conflict(&res);
- if (err) {
- platform_device_put(pdev[i]);
- pdev[i] = NULL;
- continue;
- }
- err = platform_device_add_resources(pdev[i], &res, 1);
- if (err)
- goto exit_device_put;
- /* platform_device_add calls probe() */
- err = platform_device_add(pdev[i]);
- if (err)
- goto exit_device_put;
- }
- if (!found) {
- err = -ENODEV;
- goto exit_unregister;
- }
- return 0;
- exit_device_put:
- platform_device_put(pdev[i]);
- exit_device_unregister:
- while (--i >= 0) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
- exit_unregister:
- platform_driver_unregister(&nct6683_driver);
- return err;
- }
- static void __exit sensors_nct6683_exit(void)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
- platform_driver_unregister(&nct6683_driver);
- }
- MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
- MODULE_DESCRIPTION("NCT6683D driver");
- MODULE_LICENSE("GPL");
- module_init(sensors_nct6683_init);
- module_exit(sensors_nct6683_exit);
|