Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * ntc_thermistor.c - NTC Thermistors
0004  *
0005  *  Copyright (C) 2010 Samsung Electronics
0006  *  MyungJoo Ham <myungjoo.ham@samsung.com>
0007  */
0008 
0009 #include <linux/slab.h>
0010 #include <linux/module.h>
0011 #include <linux/math64.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/property.h>
0015 #include <linux/err.h>
0016 #include <linux/fixp-arith.h>
0017 #include <linux/iio/consumer.h>
0018 #include <linux/hwmon.h>
0019 
0020 enum ntc_thermistor_type {
0021     TYPE_B57330V2103,
0022     TYPE_B57891S0103,
0023     TYPE_NCPXXWB473,
0024     TYPE_NCPXXWF104,
0025     TYPE_NCPXXWL333,
0026     TYPE_NCPXXXH103,
0027 };
0028 
0029 struct ntc_compensation {
0030     int     temp_c;
0031     unsigned int    ohm;
0032 };
0033 
0034 /*
0035  * Used as index in a zero-terminated array, holes not allowed so
0036  * that NTC_LAST is the first empty array entry.
0037  */
0038 enum {
0039     NTC_B57330V2103,
0040     NTC_B57891S0103,
0041     NTC_NCP03WB473,
0042     NTC_NCP03WF104,
0043     NTC_NCP15WB473,
0044     NTC_NCP15WL333,
0045     NTC_NCP15XH103,
0046     NTC_NCP18WB473,
0047     NTC_NCP21WB473,
0048     NTC_SSG1404001221,
0049     NTC_LAST,
0050 };
0051 
0052 static const struct platform_device_id ntc_thermistor_id[] = {
0053     [NTC_B57330V2103]     = { "b57330v2103",     TYPE_B57330V2103 },
0054     [NTC_B57891S0103]     = { "b57891s0103",     TYPE_B57891S0103 },
0055     [NTC_NCP03WB473]      = { "ncp03wb473",      TYPE_NCPXXWB473 },
0056     [NTC_NCP03WF104]      = { "ncp03wf104",      TYPE_NCPXXWF104 },
0057     [NTC_NCP15WB473]      = { "ncp15wb473",      TYPE_NCPXXWB473 },
0058     [NTC_NCP15WL333]      = { "ncp15wl333",      TYPE_NCPXXWL333 },
0059     [NTC_NCP15XH103]      = { "ncp15xh103",      TYPE_NCPXXXH103 },
0060     [NTC_NCP18WB473]      = { "ncp18wb473",      TYPE_NCPXXWB473 },
0061     [NTC_NCP21WB473]      = { "ncp21wb473",      TYPE_NCPXXWB473 },
0062     [NTC_SSG1404001221]   = { "ssg1404_001221",  TYPE_NCPXXWB473 },
0063     [NTC_LAST]            = { },
0064 };
0065 
0066 /*
0067  * A compensation table should be sorted by the values of .ohm
0068  * in descending order.
0069  * The following compensation tables are from the specification of Murata NTC
0070  * Thermistors Datasheet
0071  */
0072 static const struct ntc_compensation ncpXXwb473[] = {
0073     { .temp_c   = -40, .ohm = 1747920 },
0074     { .temp_c   = -35, .ohm = 1245428 },
0075     { .temp_c   = -30, .ohm = 898485 },
0076     { .temp_c   = -25, .ohm = 655802 },
0077     { .temp_c   = -20, .ohm = 483954 },
0078     { .temp_c   = -15, .ohm = 360850 },
0079     { .temp_c   = -10, .ohm = 271697 },
0080     { .temp_c   = -5, .ohm  = 206463 },
0081     { .temp_c   = 0, .ohm   = 158214 },
0082     { .temp_c   = 5, .ohm   = 122259 },
0083     { .temp_c   = 10, .ohm  = 95227 },
0084     { .temp_c   = 15, .ohm  = 74730 },
0085     { .temp_c   = 20, .ohm  = 59065 },
0086     { .temp_c   = 25, .ohm  = 47000 },
0087     { .temp_c   = 30, .ohm  = 37643 },
0088     { .temp_c   = 35, .ohm  = 30334 },
0089     { .temp_c   = 40, .ohm  = 24591 },
0090     { .temp_c   = 45, .ohm  = 20048 },
0091     { .temp_c   = 50, .ohm  = 16433 },
0092     { .temp_c   = 55, .ohm  = 13539 },
0093     { .temp_c   = 60, .ohm  = 11209 },
0094     { .temp_c   = 65, .ohm  = 9328 },
0095     { .temp_c   = 70, .ohm  = 7798 },
0096     { .temp_c   = 75, .ohm  = 6544 },
0097     { .temp_c   = 80, .ohm  = 5518 },
0098     { .temp_c   = 85, .ohm  = 4674 },
0099     { .temp_c   = 90, .ohm  = 3972 },
0100     { .temp_c   = 95, .ohm  = 3388 },
0101     { .temp_c   = 100, .ohm = 2902 },
0102     { .temp_c   = 105, .ohm = 2494 },
0103     { .temp_c   = 110, .ohm = 2150 },
0104     { .temp_c   = 115, .ohm = 1860 },
0105     { .temp_c   = 120, .ohm = 1615 },
0106     { .temp_c   = 125, .ohm = 1406 },
0107 };
0108 static const struct ntc_compensation ncpXXwl333[] = {
0109     { .temp_c   = -40, .ohm = 1610154 },
0110     { .temp_c   = -35, .ohm = 1130850 },
0111     { .temp_c   = -30, .ohm = 802609 },
0112     { .temp_c   = -25, .ohm = 575385 },
0113     { .temp_c   = -20, .ohm = 416464 },
0114     { .temp_c   = -15, .ohm = 304219 },
0115     { .temp_c   = -10, .ohm = 224193 },
0116     { .temp_c   = -5, .ohm  = 166623 },
0117     { .temp_c   = 0, .ohm   = 124850 },
0118     { .temp_c   = 5, .ohm   = 94287 },
0119     { .temp_c   = 10, .ohm  = 71747 },
0120     { .temp_c   = 15, .ohm  = 54996 },
0121     { .temp_c   = 20, .ohm  = 42455 },
0122     { .temp_c   = 25, .ohm  = 33000 },
0123     { .temp_c   = 30, .ohm  = 25822 },
0124     { .temp_c   = 35, .ohm  = 20335 },
0125     { .temp_c   = 40, .ohm  = 16115 },
0126     { .temp_c   = 45, .ohm  = 12849 },
0127     { .temp_c   = 50, .ohm  = 10306 },
0128     { .temp_c   = 55, .ohm  = 8314 },
0129     { .temp_c   = 60, .ohm  = 6746 },
0130     { .temp_c   = 65, .ohm  = 5503 },
0131     { .temp_c   = 70, .ohm  = 4513 },
0132     { .temp_c   = 75, .ohm  = 3721 },
0133     { .temp_c   = 80, .ohm  = 3084 },
0134     { .temp_c   = 85, .ohm  = 2569 },
0135     { .temp_c   = 90, .ohm  = 2151 },
0136     { .temp_c   = 95, .ohm  = 1809 },
0137     { .temp_c   = 100, .ohm = 1529 },
0138     { .temp_c   = 105, .ohm = 1299 },
0139     { .temp_c   = 110, .ohm = 1108 },
0140     { .temp_c   = 115, .ohm = 949 },
0141     { .temp_c   = 120, .ohm = 817 },
0142     { .temp_c   = 125, .ohm = 707 },
0143 };
0144 
0145 static const struct ntc_compensation ncpXXwf104[] = {
0146     { .temp_c   = -40, .ohm = 4397119 },
0147     { .temp_c   = -35, .ohm = 3088599 },
0148     { .temp_c   = -30, .ohm = 2197225 },
0149     { .temp_c   = -25, .ohm = 1581881 },
0150     { .temp_c   = -20, .ohm = 1151037 },
0151     { .temp_c   = -15, .ohm = 846579 },
0152     { .temp_c   = -10, .ohm = 628988 },
0153     { .temp_c   = -5, .ohm  = 471632 },
0154     { .temp_c   = 0, .ohm   = 357012 },
0155     { .temp_c   = 5, .ohm   = 272500 },
0156     { .temp_c   = 10, .ohm  = 209710 },
0157     { .temp_c   = 15, .ohm  = 162651 },
0158     { .temp_c   = 20, .ohm  = 127080 },
0159     { .temp_c   = 25, .ohm  = 100000 },
0160     { .temp_c   = 30, .ohm  = 79222 },
0161     { .temp_c   = 35, .ohm  = 63167 },
0162     { .temp_c   = 40, .ohm  = 50677 },
0163     { .temp_c   = 45, .ohm  = 40904 },
0164     { .temp_c   = 50, .ohm  = 33195 },
0165     { .temp_c   = 55, .ohm  = 27091 },
0166     { .temp_c   = 60, .ohm  = 22224 },
0167     { .temp_c   = 65, .ohm  = 18323 },
0168     { .temp_c   = 70, .ohm  = 15184 },
0169     { .temp_c   = 75, .ohm  = 12635 },
0170     { .temp_c   = 80, .ohm  = 10566 },
0171     { .temp_c   = 85, .ohm  = 8873 },
0172     { .temp_c   = 90, .ohm  = 7481 },
0173     { .temp_c   = 95, .ohm  = 6337 },
0174     { .temp_c   = 100, .ohm = 5384 },
0175     { .temp_c   = 105, .ohm = 4594 },
0176     { .temp_c   = 110, .ohm = 3934 },
0177     { .temp_c   = 115, .ohm = 3380 },
0178     { .temp_c   = 120, .ohm = 2916 },
0179     { .temp_c   = 125, .ohm = 2522 },
0180 };
0181 
0182 static const struct ntc_compensation ncpXXxh103[] = {
0183     { .temp_c   = -40, .ohm = 247565 },
0184     { .temp_c   = -35, .ohm = 181742 },
0185     { .temp_c   = -30, .ohm = 135128 },
0186     { .temp_c   = -25, .ohm = 101678 },
0187     { .temp_c   = -20, .ohm = 77373 },
0188     { .temp_c   = -15, .ohm = 59504 },
0189     { .temp_c   = -10, .ohm = 46222 },
0190     { .temp_c   = -5, .ohm  = 36244 },
0191     { .temp_c   = 0, .ohm   = 28674 },
0192     { .temp_c   = 5, .ohm   = 22878 },
0193     { .temp_c   = 10, .ohm  = 18399 },
0194     { .temp_c   = 15, .ohm  = 14910 },
0195     { .temp_c   = 20, .ohm  = 12169 },
0196     { .temp_c   = 25, .ohm  = 10000 },
0197     { .temp_c   = 30, .ohm  = 8271 },
0198     { .temp_c   = 35, .ohm  = 6883 },
0199     { .temp_c   = 40, .ohm  = 5762 },
0200     { .temp_c   = 45, .ohm  = 4851 },
0201     { .temp_c   = 50, .ohm  = 4105 },
0202     { .temp_c   = 55, .ohm  = 3492 },
0203     { .temp_c   = 60, .ohm  = 2985 },
0204     { .temp_c   = 65, .ohm  = 2563 },
0205     { .temp_c   = 70, .ohm  = 2211 },
0206     { .temp_c   = 75, .ohm  = 1915 },
0207     { .temp_c   = 80, .ohm  = 1666 },
0208     { .temp_c   = 85, .ohm  = 1454 },
0209     { .temp_c   = 90, .ohm  = 1275 },
0210     { .temp_c   = 95, .ohm  = 1121 },
0211     { .temp_c   = 100, .ohm = 990 },
0212     { .temp_c   = 105, .ohm = 876 },
0213     { .temp_c   = 110, .ohm = 779 },
0214     { .temp_c   = 115, .ohm = 694 },
0215     { .temp_c   = 120, .ohm = 620 },
0216     { .temp_c   = 125, .ohm = 556 },
0217 };
0218 
0219 /*
0220  * The following compensation tables are from the specifications in EPCOS NTC
0221  * Thermistors Datasheets
0222  */
0223 static const struct ntc_compensation b57330v2103[] = {
0224     { .temp_c   = -40, .ohm = 190030 },
0225     { .temp_c   = -35, .ohm = 145360 },
0226     { .temp_c   = -30, .ohm = 112060 },
0227     { .temp_c   = -25, .ohm = 87041 },
0228     { .temp_c   = -20, .ohm = 68104 },
0229     { .temp_c   = -15, .ohm = 53665 },
0230     { .temp_c   = -10, .ohm = 42576 },
0231     { .temp_c   = -5, .ohm  = 34001 },
0232     { .temp_c   = 0, .ohm   = 27326 },
0233     { .temp_c   = 5, .ohm   = 22096 },
0234     { .temp_c   = 10, .ohm  = 17973 },
0235     { .temp_c   = 15, .ohm  = 14703 },
0236     { .temp_c   = 20, .ohm  = 12090 },
0237     { .temp_c   = 25, .ohm  = 10000 },
0238     { .temp_c   = 30, .ohm  = 8311 },
0239     { .temp_c   = 35, .ohm  = 6941 },
0240     { .temp_c   = 40, .ohm  = 5825 },
0241     { .temp_c   = 45, .ohm  = 4911 },
0242     { .temp_c   = 50, .ohm  = 4158 },
0243     { .temp_c   = 55, .ohm  = 3536 },
0244     { .temp_c   = 60, .ohm  = 3019 },
0245     { .temp_c   = 65, .ohm  = 2588 },
0246     { .temp_c   = 70, .ohm  = 2227 },
0247     { .temp_c   = 75, .ohm  = 1924 },
0248     { .temp_c   = 80, .ohm  = 1668 },
0249     { .temp_c   = 85, .ohm  = 1451 },
0250     { .temp_c   = 90, .ohm  = 1266 },
0251     { .temp_c   = 95, .ohm  = 1108 },
0252     { .temp_c   = 100, .ohm = 973 },
0253     { .temp_c   = 105, .ohm = 857 },
0254     { .temp_c   = 110, .ohm = 757 },
0255     { .temp_c   = 115, .ohm = 671 },
0256     { .temp_c   = 120, .ohm = 596 },
0257     { .temp_c   = 125, .ohm = 531 },
0258 };
0259 
0260 static const struct ntc_compensation b57891s0103[] = {
0261     { .temp_c   = -55.0, .ohm   = 878900 },
0262     { .temp_c   = -50.0, .ohm   = 617590 },
0263     { .temp_c   = -45.0, .ohm   = 439340 },
0264     { .temp_c   = -40.0, .ohm   = 316180 },
0265     { .temp_c   = -35.0, .ohm   = 230060 },
0266     { .temp_c   = -30.0, .ohm   = 169150 },
0267     { .temp_c   = -25.0, .ohm   = 125550 },
0268     { .temp_c   = -20.0, .ohm   = 94143 },
0269     { .temp_c   = -15.0, .ohm   = 71172 },
0270     { .temp_c   = -10.0, .ohm   = 54308 },
0271     { .temp_c   = -5.0, .ohm    = 41505 },
0272     { .temp_c   = 0.0, .ohm = 32014 },
0273     { .temp_c   = 5.0, .ohm = 25011 },
0274     { .temp_c   = 10.0, .ohm    = 19691 },
0275     { .temp_c   = 15.0, .ohm    = 15618 },
0276     { .temp_c   = 20.0, .ohm    = 12474 },
0277     { .temp_c   = 25.0, .ohm    = 10000 },
0278     { .temp_c   = 30.0, .ohm    = 8080 },
0279     { .temp_c   = 35.0, .ohm    = 6569 },
0280     { .temp_c   = 40.0, .ohm    = 5372 },
0281     { .temp_c   = 45.0, .ohm    = 4424 },
0282     { .temp_c   = 50.0, .ohm    = 3661 },
0283     { .temp_c   = 55.0, .ohm    = 3039 },
0284     { .temp_c   = 60.0, .ohm    = 2536 },
0285     { .temp_c   = 65.0, .ohm    = 2128 },
0286     { .temp_c   = 70.0, .ohm    = 1794 },
0287     { .temp_c   = 75.0, .ohm    = 1518 },
0288     { .temp_c   = 80.0, .ohm    = 1290 },
0289     { .temp_c   = 85.0, .ohm    = 1100 },
0290     { .temp_c   = 90.0, .ohm    = 942 },
0291     { .temp_c   = 95.0, .ohm    = 809 },
0292     { .temp_c   = 100.0, .ohm   = 697 },
0293     { .temp_c   = 105.0, .ohm   = 604 },
0294     { .temp_c   = 110.0, .ohm   = 525 },
0295     { .temp_c   = 115.0, .ohm   = 457 },
0296     { .temp_c   = 120.0, .ohm   = 400 },
0297     { .temp_c   = 125.0, .ohm   = 351 },
0298     { .temp_c   = 130.0, .ohm   = 308 },
0299     { .temp_c   = 135.0, .ohm   = 272 },
0300     { .temp_c   = 140.0, .ohm   = 240 },
0301     { .temp_c   = 145.0, .ohm   = 213 },
0302     { .temp_c   = 150.0, .ohm   = 189 },
0303     { .temp_c   = 155.0, .ohm   = 168 },
0304 };
0305 
0306 struct ntc_type {
0307     const struct ntc_compensation *comp;
0308     int n_comp;
0309 };
0310 
0311 #define NTC_TYPE(ntc, compensation) \
0312 [(ntc)] = { .comp = (compensation), .n_comp = ARRAY_SIZE(compensation) }
0313 
0314 static const struct ntc_type ntc_type[] = {
0315     NTC_TYPE(TYPE_B57330V2103, b57330v2103),
0316     NTC_TYPE(TYPE_B57891S0103, b57891s0103),
0317     NTC_TYPE(TYPE_NCPXXWB473,  ncpXXwb473),
0318     NTC_TYPE(TYPE_NCPXXWF104,  ncpXXwf104),
0319     NTC_TYPE(TYPE_NCPXXWL333,  ncpXXwl333),
0320     NTC_TYPE(TYPE_NCPXXXH103,  ncpXXxh103),
0321 };
0322 
0323 /*
0324  * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required.
0325  *
0326  * How to setup pullup_ohm, pulldown_ohm, and connect is
0327  * described at Documentation/hwmon/ntc_thermistor.rst
0328  *
0329  * pullup/down_ohm: 0 for infinite / not-connected
0330  *
0331  * chan: iio_channel pointer to communicate with the ADC which the
0332  * thermistor is using for conversion of the analog values.
0333  */
0334 struct ntc_data {
0335     const struct ntc_compensation *comp;
0336     int n_comp;
0337     unsigned int pullup_uv;
0338     unsigned int pullup_ohm;
0339     unsigned int pulldown_ohm;
0340     enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect;
0341     struct iio_channel *chan;
0342 };
0343 
0344 static int ntc_adc_iio_read(struct ntc_data *data)
0345 {
0346     struct iio_channel *channel = data->chan;
0347     int uv, ret;
0348 
0349     ret = iio_read_channel_processed_scale(channel, &uv, 1000);
0350     if (ret < 0) {
0351         int raw;
0352 
0353         /*
0354          * This fallback uses a raw read and then
0355          * assumes the ADC is 12 bits, scaling with
0356          * a factor 1000 to get to microvolts.
0357          */
0358         ret = iio_read_channel_raw(channel, &raw);
0359         if (ret < 0) {
0360             pr_err("read channel() error: %d\n", ret);
0361             return ret;
0362         }
0363         ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000);
0364         if (ret < 0) {
0365             /* Assume 12 bit ADC with vref at pullup_uv */
0366             uv = (data->pullup_uv * (s64)raw) >> 12;
0367         }
0368     }
0369 
0370     return uv;
0371 }
0372 
0373 static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
0374 {
0375     if (divisor == 0 && dividend == 0)
0376         return 0;
0377     if (divisor == 0)
0378         return UINT_MAX;
0379     return div64_u64(dividend, divisor);
0380 }
0381 
0382 static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
0383 {
0384     u32 puv = data->pullup_uv;
0385     u64 n, puo, pdo;
0386     puo = data->pullup_ohm;
0387     pdo = data->pulldown_ohm;
0388 
0389     if (uv == 0)
0390         return (data->connect == NTC_CONNECTED_POSITIVE) ?
0391             INT_MAX : 0;
0392     if (uv >= puv)
0393         return (data->connect == NTC_CONNECTED_POSITIVE) ?
0394             0 : INT_MAX;
0395 
0396     if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0)
0397         n = div_u64(pdo * (puv - uv), uv);
0398     else if (data->connect == NTC_CONNECTED_GROUND && pdo == 0)
0399         n = div_u64(puo * uv, puv - uv);
0400     else if (data->connect == NTC_CONNECTED_POSITIVE)
0401         n = div64_u64_safe(pdo * puo * (puv - uv),
0402                 puo * uv - pdo * (puv - uv));
0403     else
0404         n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
0405 
0406     if (n > INT_MAX)
0407         n = INT_MAX;
0408     return n;
0409 }
0410 
0411 static void lookup_comp(struct ntc_data *data, unsigned int ohm,
0412             int *i_low, int *i_high)
0413 {
0414     int start, end, mid;
0415 
0416     /*
0417      * Handle special cases: Resistance is higher than or equal to
0418      * resistance in first table entry, or resistance is lower or equal
0419      * to resistance in last table entry.
0420      * In these cases, return i_low == i_high, either pointing to the
0421      * beginning or to the end of the table depending on the condition.
0422      */
0423     if (ohm >= data->comp[0].ohm) {
0424         *i_low = 0;
0425         *i_high = 0;
0426         return;
0427     }
0428     if (ohm <= data->comp[data->n_comp - 1].ohm) {
0429         *i_low = data->n_comp - 1;
0430         *i_high = data->n_comp - 1;
0431         return;
0432     }
0433 
0434     /* Do a binary search on compensation table */
0435     start = 0;
0436     end = data->n_comp;
0437     while (start < end) {
0438         mid = start + (end - start) / 2;
0439         /*
0440          * start <= mid < end
0441          * data->comp[start].ohm > ohm >= data->comp[end].ohm
0442          *
0443          * We could check for "ohm == data->comp[mid].ohm" here, but
0444          * that is a quite unlikely condition, and we would have to
0445          * check again after updating start. Check it at the end instead
0446          * for simplicity.
0447          */
0448         if (ohm >= data->comp[mid].ohm) {
0449             end = mid;
0450         } else {
0451             start = mid + 1;
0452             /*
0453              * ohm >= data->comp[start].ohm might be true here,
0454              * since we set start to mid + 1. In that case, we are
0455              * done. We could keep going, but the condition is quite
0456              * likely to occur, so it is worth checking for it.
0457              */
0458             if (ohm >= data->comp[start].ohm)
0459                 end = start;
0460         }
0461         /*
0462          * start <= end
0463          * data->comp[start].ohm >= ohm >= data->comp[end].ohm
0464          */
0465     }
0466     /*
0467      * start == end
0468      * ohm >= data->comp[end].ohm
0469      */
0470     *i_low = end;
0471     if (ohm == data->comp[end].ohm)
0472         *i_high = end;
0473     else
0474         *i_high = end - 1;
0475 }
0476 
0477 static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
0478 {
0479     int low, high;
0480     int temp;
0481 
0482     lookup_comp(data, ohm, &low, &high);
0483     /*
0484      * First multiplying the table temperatures with 1000 to get to
0485      * millicentigrades (which is what we want) and then interpolating
0486      * will give the best precision.
0487      */
0488     temp = fixp_linear_interpolate(data->comp[low].ohm,
0489                        data->comp[low].temp_c * 1000,
0490                        data->comp[high].ohm,
0491                        data->comp[high].temp_c * 1000,
0492                        ohm);
0493     return temp;
0494 }
0495 
0496 static int ntc_thermistor_get_ohm(struct ntc_data *data)
0497 {
0498     int read_uv;
0499 
0500     read_uv = ntc_adc_iio_read(data);
0501     if (read_uv < 0)
0502         return read_uv;
0503     return get_ohm_of_thermistor(data, read_uv);
0504 }
0505 
0506 static int ntc_read(struct device *dev, enum hwmon_sensor_types type,
0507             u32 attr, int channel, long *val)
0508 {
0509     struct ntc_data *data = dev_get_drvdata(dev);
0510     int ohm;
0511 
0512     switch (type) {
0513     case hwmon_temp:
0514         switch (attr) {
0515         case hwmon_temp_input:
0516             ohm = ntc_thermistor_get_ohm(data);
0517             if (ohm < 0)
0518                 return ohm;
0519             *val = get_temp_mc(data, ohm);
0520             return 0;
0521         case hwmon_temp_type:
0522             *val = 4;
0523             return 0;
0524         default:
0525             break;
0526         }
0527         break;
0528     default:
0529         break;
0530     }
0531     return -EINVAL;
0532 }
0533 
0534 static umode_t ntc_is_visible(const void *data, enum hwmon_sensor_types type,
0535                   u32 attr, int channel)
0536 {
0537     if (type == hwmon_temp) {
0538         switch (attr) {
0539         case hwmon_temp_input:
0540         case hwmon_temp_type:
0541             return 0444;
0542         default:
0543             break;
0544         }
0545     }
0546     return 0;
0547 }
0548 
0549 static const struct hwmon_channel_info *ntc_info[] = {
0550     HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
0551     HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_TYPE),
0552     NULL
0553 };
0554 
0555 static const struct hwmon_ops ntc_hwmon_ops = {
0556     .is_visible = ntc_is_visible,
0557     .read = ntc_read,
0558 };
0559 
0560 static const struct hwmon_chip_info ntc_chip_info = {
0561     .ops = &ntc_hwmon_ops,
0562     .info = ntc_info,
0563 };
0564 
0565 static int ntc_thermistor_parse_props(struct device *dev,
0566                       struct ntc_data *data)
0567 {
0568     struct iio_channel *chan;
0569     enum iio_chan_type type;
0570     int ret;
0571 
0572     chan = devm_iio_channel_get(dev, NULL);
0573     if (IS_ERR(chan))
0574         return PTR_ERR(chan);
0575 
0576     ret = iio_get_channel_type(chan, &type);
0577     if (ret < 0)
0578         return ret;
0579 
0580     if (type != IIO_VOLTAGE)
0581         return -EINVAL;
0582 
0583     ret = device_property_read_u32(dev, "pullup-uv", &data->pullup_uv);
0584     if (ret)
0585         return dev_err_probe(dev,  ret, "pullup-uv not specified\n");
0586 
0587     ret = device_property_read_u32(dev, "pullup-ohm", &data->pullup_ohm);
0588     if (ret)
0589         return dev_err_probe(dev,  ret, "pullup-ohm not specified\n");
0590 
0591     ret = device_property_read_u32(dev, "pulldown-ohm", &data->pulldown_ohm);
0592     if (ret)
0593         return dev_err_probe(dev,  ret, "pulldown-ohm not specified\n");
0594 
0595     if (device_property_read_bool(dev, "connected-positive"))
0596         data->connect = NTC_CONNECTED_POSITIVE;
0597     else /* status change should be possible if not always on. */
0598         data->connect = NTC_CONNECTED_GROUND;
0599 
0600     data->chan = chan;
0601 
0602     return 0;
0603 }
0604 
0605 static int ntc_thermistor_probe(struct platform_device *pdev)
0606 {
0607     struct device *dev = &pdev->dev;
0608     const struct platform_device_id *pdev_id;
0609     struct device *hwmon_dev;
0610     struct ntc_data *data;
0611     int ret;
0612 
0613     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0614     if (!data)
0615         return -ENOMEM;
0616 
0617     ret = ntc_thermistor_parse_props(dev, data);
0618     if (ret)
0619         return ret;
0620 
0621     if (data->pullup_uv == 0 ||
0622         (data->pullup_ohm == 0 && data->connect ==
0623          NTC_CONNECTED_GROUND) ||
0624         (data->pulldown_ohm == 0 && data->connect ==
0625          NTC_CONNECTED_POSITIVE) ||
0626         (data->connect != NTC_CONNECTED_POSITIVE &&
0627          data->connect != NTC_CONNECTED_GROUND)) {
0628         dev_err(dev, "Required data to use NTC driver not supplied.\n");
0629         return -EINVAL;
0630     }
0631 
0632     pdev_id = device_get_match_data(dev);
0633 
0634     if (pdev_id->driver_data >= ARRAY_SIZE(ntc_type)) {
0635         dev_err(dev, "Unknown device type: %lu(%s)\n",
0636                 pdev_id->driver_data, pdev_id->name);
0637         return -EINVAL;
0638     }
0639 
0640     data->comp   = ntc_type[pdev_id->driver_data].comp;
0641     data->n_comp = ntc_type[pdev_id->driver_data].n_comp;
0642 
0643     hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev_id->name,
0644                              data, &ntc_chip_info,
0645                              NULL);
0646     if (IS_ERR(hwmon_dev)) {
0647         dev_err(dev, "unable to register as hwmon device.\n");
0648         return PTR_ERR(hwmon_dev);
0649     }
0650 
0651     dev_info(dev, "Thermistor type: %s successfully probed.\n",
0652          pdev_id->name);
0653 
0654     return 0;
0655 }
0656 
0657 static const struct of_device_id ntc_match[] = {
0658     { .compatible = "epcos,b57330v2103",
0659         .data = &ntc_thermistor_id[NTC_B57330V2103]},
0660     { .compatible = "epcos,b57891s0103",
0661         .data = &ntc_thermistor_id[NTC_B57891S0103] },
0662     { .compatible = "murata,ncp03wb473",
0663         .data = &ntc_thermistor_id[NTC_NCP03WB473] },
0664     { .compatible = "murata,ncp03wf104",
0665         .data = &ntc_thermistor_id[NTC_NCP03WF104] },
0666     { .compatible = "murata,ncp15wb473",
0667         .data = &ntc_thermistor_id[NTC_NCP15WB473] },
0668     { .compatible = "murata,ncp15wl333",
0669         .data = &ntc_thermistor_id[NTC_NCP15WL333] },
0670     { .compatible = "murata,ncp15xh103",
0671         .data = &ntc_thermistor_id[NTC_NCP15XH103] },
0672     { .compatible = "murata,ncp18wb473",
0673         .data = &ntc_thermistor_id[NTC_NCP18WB473] },
0674     { .compatible = "murata,ncp21wb473",
0675         .data = &ntc_thermistor_id[NTC_NCP21WB473] },
0676     { .compatible = "samsung,1404-001221",
0677         .data = &ntc_thermistor_id[NTC_SSG1404001221] },
0678 
0679     /* Usage of vendor name "ntc" is deprecated */
0680     { .compatible = "ntc,ncp03wb473",
0681         .data = &ntc_thermistor_id[NTC_NCP03WB473] },
0682     { .compatible = "ntc,ncp15wb473",
0683         .data = &ntc_thermistor_id[NTC_NCP15WB473] },
0684     { .compatible = "ntc,ncp15wl333",
0685         .data = &ntc_thermistor_id[NTC_NCP15WL333] },
0686     { .compatible = "ntc,ncp18wb473",
0687         .data = &ntc_thermistor_id[NTC_NCP18WB473] },
0688     { .compatible = "ntc,ncp21wb473",
0689         .data = &ntc_thermistor_id[NTC_NCP21WB473] },
0690     { },
0691 };
0692 MODULE_DEVICE_TABLE(of, ntc_match);
0693 
0694 static struct platform_driver ntc_thermistor_driver = {
0695     .driver = {
0696         .name = "ntc-thermistor",
0697         .of_match_table = ntc_match,
0698     },
0699     .probe = ntc_thermistor_probe,
0700     .id_table = ntc_thermistor_id,
0701 };
0702 
0703 module_platform_driver(ntc_thermistor_driver);
0704 
0705 MODULE_DESCRIPTION("NTC Thermistor Driver");
0706 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
0707 MODULE_LICENSE("GPL");
0708 MODULE_ALIAS("platform:ntc-thermistor");