coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
lm96000.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <delay.h>
4 #include <timer.h>
5 #include <console/console.h>
6 #include <device/device.h>
7 #include <device/i2c_bus.h>
8 
9 #include "lm96000.h"
10 #include "chip.h"
11 
12 static inline int lm96000_read(struct device *const dev, const u8 reg)
13 {
14  return i2c_dev_readb_at(dev, reg);
15 }
16 
17 static inline int lm96000_write(struct device *const dev,
18  const u8 reg, const u8 value)
19 {
20  return i2c_dev_writeb_at(dev, reg, value);
21 }
22 
23 static inline int lm96000_update(struct device *const dev, const u8 reg,
24  const u8 clear_mask, const u8 set_mask)
25 {
26  const int val = i2c_dev_readb_at(dev, reg);
27  if (val < 0)
28  return val;
29  return i2c_dev_writeb_at(dev, reg, (val & ~clear_mask) | set_mask);
30 }
31 
32 static const unsigned int ref_mv[] = { 2500, 2250, 3300, 5000, 12000 };
33 
34 static u8 lm96000_to_low_limit(const enum lm96000_vin ref, const u16 limit)
35 {
36  const unsigned int reg =
37  (unsigned int)limit * 0xc0 / ref_mv[ref];
38  return reg < 0xff ? reg : 0xff;
39 }
40 
41 static u8 lm96000_to_high_limit(const enum lm96000_vin ref, const u16 limit)
42 {
43  const unsigned int reg =
44  DIV_ROUND_UP((unsigned int)limit * 0xc0, ref_mv[ref]);
45  return reg < 0xff ? reg : 0xff;
46 }
47 
48 static void lm96000_set_vin_limits(struct device *const dev,
49  const struct drivers_i2c_lm96000_config *const config)
50 {
51  unsigned int i;
52 
53  for (i = 0; i < LM96000_VIN_CNT; ++i) {
55  lm96000_to_low_limit(i, config->vin[i].low));
56  if (config->vin[i].high > config->vin[i].low)
58  lm96000_to_high_limit(i, config->vin[i].high));
59  else
61  }
62 }
63 
64 static void lm96000_set_temp_limits(struct device *const dev,
65  const struct drivers_i2c_lm96000_config *const config)
66 {
67  unsigned int i;
68 
69  for (i = 0; i < LM96000_TEMP_IN_CNT; ++i) {
71  config->temp_in[i].low);
72  if (config->temp_in[i].high > config->temp_in[i].low)
74  config->temp_in[i].high);
75  else
77  }
78 }
79 
80 static u16 lm96000_rpm_to_tach(const u16 rpm)
81 {
82  return rpm ? (60 * 90000 / rpm) & 0xfffc : 0xfffc;
83 }
84 
85 static void lm96000_set_fan_limits(struct device *const dev,
86  const struct drivers_i2c_lm96000_config *const config)
87 {
88  unsigned int i;
89 
90  for (i = 0; i < LM96000_FAN_IN_CNT; ++i) {
91  const u16 tach = lm96000_rpm_to_tach(config->fan_in[i].low);
92  lm96000_write(dev, LM96000_FAN_LOW_LIMIT(i), tach & 0xff);
93  lm96000_write(dev, LM96000_FAN_LOW_LIMIT(i) + 1, tach >> 8);
94  }
95 }
96 
97 static u8 lm96000_to_duty(const u8 duty_cycle)
98 {
99  return duty_cycle * 255 / 100;
100 }
101 
102 static void lm96000_configure_pwm(struct device *const dev,
103  const unsigned int fan,
104  const struct lm96000_fan_config *const config)
105 {
111  (config->invert ? LM96000_FAN_CFG_PWM_INVERT : 0) |
112  config->spinup);
117  config->freq <= LM96000_PWM_94HZ
118  ? config->tach << LM96000_TACH_MODE_FAN_SHIFT(fan) : 0);
119 
120  switch (config->mode) {
127  lm96000_to_duty(config->min_duty));
128  break;
129  case LM96000_FAN_MANUAL:
131  lm96000_to_duty(config->duty_cycle));
132  break;
133  default:
134  break;
135  }
136 }
137 
138 static void lm96000_configure_temp_zone(struct device *const dev,
139  const unsigned int zone,
140  const struct lm96000_temp_zone *const config)
141 {
142  static const u8 temp_range[] =
143  { 2, 3, 3, 4, 5, 7, 8, 10, 13, 16, 20, 27, 32, 40, 53, 80 };
144  unsigned int i;
145 
146  /* find longest range that starts from `low_temp` */
147  for (i = ARRAY_SIZE(temp_range) - 1; i > 0; --i) {
148  if (config->low_temp + temp_range[i] <= config->target_temp)
149  break;
150  }
151 
155  config->target_temp >= temp_range[i]
156  ? config->target_temp - temp_range[i]
157  : 0);
159  config->panic_temp ? config->panic_temp : 100);
162  LM96000_ZONE_SMOOTH_EN(zone) | 0); /* 0: 35s */
164  LM96000_FAN_MIN(zone),
165  config->min_off ? LM96000_FAN_MIN(zone) : 0);
168  config->hysteresis << LM96000_ZONE_HYST_SHIFT(zone)
169  & LM96000_ZONE_HYST_MASK(zone));
170 }
171 
172 static void lm96000_init(struct device *const dev)
173 {
174  const struct drivers_i2c_lm96000_config *const config = dev->chip_info;
175  unsigned int i;
176  int lm_config;
177  struct stopwatch sw;
178 
179  printk(BIOS_DEBUG, "lm96000: Initialization hardware monitoring.\n");
180 
181  stopwatch_init_msecs_expire(&sw, 1000);
182  lm_config = lm96000_read(dev, LM96000_CONFIG);
183  while ((lm_config < 0 || !((unsigned int)lm_config & LM96000_READY))) {
184  mdelay(1);
185  lm_config = lm96000_read(dev, LM96000_CONFIG);
186  if (stopwatch_expired(&sw))
187  break;
188  }
189  if (lm_config < 0 || !((unsigned int)lm_config & LM96000_READY)) {
190  printk(BIOS_INFO, "lm96000: Not ready after 1s.\n");
191  return;
192  }
193 
197  for (i = 0; i < LM96000_PWM_CTL_CNT; ++i) {
198  if (config->fan[i].mode != LM96000_FAN_IGNORE)
199  lm96000_configure_pwm(dev, i, config->fan + i);
200  }
201  for (i = 0; i < LM96000_TEMP_ZONE_CNT; ++i)
202  lm96000_configure_temp_zone(dev, i, config->zone + i);
204 }
205 
206 static struct device_operations lm96000_ops = {
208  .set_resources = noop_set_resources,
209  .init = lm96000_init,
210 };
211 
212 static void lm96000_enable(struct device *const dev)
213 {
214  dev->ops = &lm96000_ops;
215 }
216 
218  CHIP_NAME("LM96000")
219  .enable_dev = lm96000_enable
220 };
pte_t value
Definition: mmu.c:91
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define DIV_ROUND_UP(x, y)
Definition: helpers.h:60
#define printk(level,...)
Definition: stdlib.h:16
void mdelay(unsigned int msecs)
Definition: delay.c:2
#define LM96000_FAN_IN_CNT
Definition: chip.h:10
lm96000_vin
Definition: chip.h:14
#define LM96000_PWM_CTL_CNT
Definition: chip.h:11
@ LM96000_FAN_ZONE_1_AUTO
Definition: chip.h:26
@ LM96000_FAN_ZONE_3_AUTO
Definition: chip.h:28
@ LM96000_FAN_HOTTEST_123
Definition: chip.h:32
@ LM96000_FAN_ZONE_2_AUTO
Definition: chip.h:27
@ LM96000_FAN_IGNORE
Definition: chip.h:25
@ LM96000_FAN_HOTTEST_23
Definition: chip.h:31
@ LM96000_FAN_MANUAL
Definition: chip.h:33
#define LM96000_TEMP_IN_CNT
Definition: chip.h:9
#define LM96000_VIN_CNT
Definition: chip.h:8
@ LM96000_PWM_94HZ
Definition: chip.h:44
#define LM96000_TEMP_ZONE_CNT
Definition: chip.h:12
int i2c_dev_writeb_at(struct device *const dev, const uint8_t off, const uint8_t val)
Definition: i2c_bus.c:121
int i2c_dev_readb_at(struct device *const dev, uint8_t off)
Definition: i2c_bus.c:84
#define CHIP_NAME(X)
Definition: device.h:32
static void noop_read_resources(struct device *dev)
Standard device operations function pointers shims.
Definition: device.h:73
static void noop_set_resources(struct device *dev)
Definition: device.h:74
static int stopwatch_expired(struct stopwatch *sw)
Definition: timer.h:152
static void stopwatch_init_msecs_expire(struct stopwatch *sw, long ms)
Definition: timer.h:133
static u8 lm96000_to_duty(const u8 duty_cycle)
Definition: lm96000.c:97
static void lm96000_set_vin_limits(struct device *const dev, const struct drivers_i2c_lm96000_config *const config)
Definition: lm96000.c:48
static void lm96000_init(struct device *const dev)
Definition: lm96000.c:172
static u16 lm96000_rpm_to_tach(const u16 rpm)
Definition: lm96000.c:80
static const unsigned int ref_mv[]
Definition: lm96000.c:32
static void lm96000_enable(struct device *const dev)
Definition: lm96000.c:212
static u8 lm96000_to_high_limit(const enum lm96000_vin ref, const u16 limit)
Definition: lm96000.c:41
static void lm96000_set_fan_limits(struct device *const dev, const struct drivers_i2c_lm96000_config *const config)
Definition: lm96000.c:85
static void lm96000_configure_pwm(struct device *const dev, const unsigned int fan, const struct lm96000_fan_config *const config)
Definition: lm96000.c:102
static int lm96000_update(struct device *const dev, const u8 reg, const u8 clear_mask, const u8 set_mask)
Definition: lm96000.c:23
static struct device_operations lm96000_ops
Definition: lm96000.c:206
static int lm96000_write(struct device *const dev, const u8 reg, const u8 value)
Definition: lm96000.c:17
static int lm96000_read(struct device *const dev, const u8 reg)
Definition: lm96000.c:12
static void lm96000_set_temp_limits(struct device *const dev, const struct drivers_i2c_lm96000_config *const config)
Definition: lm96000.c:64
static u8 lm96000_to_low_limit(const enum lm96000_vin ref, const u16 limit)
Definition: lm96000.c:34
static void lm96000_configure_temp_zone(struct device *const dev, const unsigned int zone, const struct lm96000_temp_zone *const config)
Definition: lm96000.c:138
struct chip_operations drivers_i2c_lm96000_ops
Definition: lm96000.c:217
#define LM96000_FAN_FREQ_MASK
Definition: lm96000.h:31
#define LM96000_TEMP_LOW_LIMIT(temp)
Definition: lm96000.h:17
#define LM96000_FAN_MIN(fan)
Definition: lm96000.h:33
#define LM96000_FAN_FREQ(fan)
Definition: lm96000.h:30
#define LM96000_ZONE_RANGE_SHIFT
Definition: lm96000.h:40
#define LM96000_ZONE_RANGE_MASK
Definition: lm96000.h:41
#define LM96000_FAN_LOW_LIMIT(fan)
Definition: lm96000.h:22
#define LM96000_ZONE_HYST_MASK(zone)
Definition: lm96000.h:50
#define LM96000_FAN_CFG_MODE_SHIFT
Definition: lm96000.h:26
#define LM96000_TACH_MODE_FAN_MASK(f)
Definition: lm96000.h:37
#define LM96000_FAN_CFG(fan)
Definition: lm96000.h:25
#define LM96000_CONFIG
Definition: lm96000.h:8
#define LM96000_TACH_MODE_FAN_SHIFT(f)
Definition: lm96000.h:36
#define LM96000_FAN_MIN_OFF
Definition: lm96000.h:32
#define LM96000_TEMP_HIGH_LIMIT(temp)
Definition: lm96000.h:18
#define LM96000_ZONE_SMOOTH(zone)
Definition: lm96000.h:42
#define LM96000_READY
Definition: lm96000.h:9
#define LM96000_START
Definition: lm96000.h:10
#define LM96000_FAN_CFG_SPINUP_MASK
Definition: lm96000.h:29
#define LM96000_VIN_HIGH_LIMIT(v)
Definition: lm96000.h:14
#define LM96000_ZONE_HYSTERESIS(zone)
Definition: lm96000.h:48
#define LM96000_TACH_MONITOR_MODE
Definition: lm96000.h:35
#define LM96000_FAN_MIN_PWM(fan)
Definition: lm96000.h:34
#define LM96000_ZONE_TEMP_LOW(zone)
Definition: lm96000.h:46
#define LM96000_FAN_CFG_MODE_MASK
Definition: lm96000.h:27
#define LM96000_VIN_LOW_LIMIT(v)
Definition: lm96000.h:13
#define LM96000_ZONE_RANGE(zone)
Definition: lm96000.h:39
#define LM96000_ZONE_HYST_SHIFT(zone)
Definition: lm96000.h:49
#define LM96000_ZONE_SMOOTH_EN(zone)
Definition: lm96000.h:43
#define LM96000_FAN_CFG_PWM_INVERT
Definition: lm96000.h:28
#define LM96000_ZONE_TEMP_PANIC(zone)
Definition: lm96000.h:47
#define LM96000_ZONE_SMOOTH_MASK(zone)
Definition: lm96000.h:45
#define LM96000_FAN_DUTY(fan)
Definition: lm96000.h:24
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
enum board_config config
Definition: memory.c:448
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
void(* read_resources)(struct device *dev)
Definition: device.h:39
Definition: device.h:107
struct device_operations * ops
Definition: device.h:143
DEVTREE_CONST void * chip_info
Definition: device.h:164
u8 val
Definition: sys.c:300