Blame view

drivers/mfd/wm8400-core.c 3.71 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1d9f9f040   Mark Brown   mfd: Core support...
2
3
4
5
6
7
  /*
   * Core driver for WM8400.
   *
   * Copyright 2008 Wolfson Microelectronics PLC.
   *
   * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
1d9f9f040   Mark Brown   mfd: Core support...
8
   */
b2b658752   Paul Gortmaker   mfd: wm8400-core:...
9
  #include <linux/init.h>
1d9f9f040   Mark Brown   mfd: Core support...
10
  #include <linux/bug.h>
50eeef5d3   Mark Brown   mfd: Convert WM84...
11
  #include <linux/err.h>
1d9f9f040   Mark Brown   mfd: Core support...
12
13
  #include <linux/i2c.h>
  #include <linux/kernel.h>
b8380c1a6   Mark Brown   mfd: Register WM8...
14
  #include <linux/mfd/core.h>
1d9f9f040   Mark Brown   mfd: Core support...
15
16
  #include <linux/mfd/wm8400-private.h>
  #include <linux/mfd/wm8400-audio.h>
50eeef5d3   Mark Brown   mfd: Convert WM84...
17
  #include <linux/regmap.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
1d9f9f040   Mark Brown   mfd: Core support...
19

879eed682   Mark Brown   mfd: Remove wm840...
20
  static bool wm8400_volatile(struct device *dev, unsigned int reg)
1d9f9f040   Mark Brown   mfd: Core support...
21
  {
879eed682   Mark Brown   mfd: Remove wm840...
22
23
24
25
26
27
28
  	switch (reg) {
  	case WM8400_INTERRUPT_STATUS_1:
  	case WM8400_INTERRUPT_LEVELS:
  	case WM8400_SHUTDOWN_REASON:
  		return true;
  	default:
  		return false;
1d9f9f040   Mark Brown   mfd: Core support...
29
  	}
1d9f9f040   Mark Brown   mfd: Core support...
30
  }
b8380c1a6   Mark Brown   mfd: Register WM8...
31
32
  static int wm8400_register_codec(struct wm8400 *wm8400)
  {
d1fb70e27   Krzysztof Kozlowski   mfd: wm8400-core:...
33
  	const struct mfd_cell cell = {
b8380c1a6   Mark Brown   mfd: Register WM8...
34
  		.name = "wm8400-codec",
e45be4b5f   Samuel Ortiz   mfd: Use mfd cell...
35
36
  		.platform_data = wm8400,
  		.pdata_size = sizeof(*wm8400),
b8380c1a6   Mark Brown   mfd: Register WM8...
37
  	};
7825dc056   Laxman Dewangan   mfd: wm8400: Use ...
38
  	return devm_mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0, NULL);
b8380c1a6   Mark Brown   mfd: Register WM8...
39
  }
1d9f9f040   Mark Brown   mfd: Core support...
40
41
42
43
44
45
46
47
48
49
  /*
   * wm8400_init - Generic initialisation
   *
   * The WM8400 can be configured as either an I2C or SPI device.  Probe
   * functions for each bus set up the accessors then call into this to
   * set up the device itself.
   */
  static int wm8400_init(struct wm8400 *wm8400,
  		       struct wm8400_platform_data *pdata)
  {
879eed682   Mark Brown   mfd: Remove wm840...
50
51
  	unsigned int reg;
  	int ret;
1d9f9f040   Mark Brown   mfd: Core support...
52

1902a9e62   Greg Kroah-Hartman   mfd: remove drive...
53
  	dev_set_drvdata(wm8400->dev, wm8400);
1d9f9f040   Mark Brown   mfd: Core support...
54
55
  
  	/* Check that this is actually a WM8400 */
879eed682   Mark Brown   mfd: Remove wm840...
56
  	ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &reg);
1d9f9f040   Mark Brown   mfd: Core support...
57
58
59
60
61
  	if (ret != 0) {
  		dev_err(wm8400->dev, "Chip ID register read failed
  ");
  		return -EIO;
  	}
879eed682   Mark Brown   mfd: Remove wm840...
62
63
64
65
  	if (reg != 0x6172) {
  		dev_err(wm8400->dev, "Device is not a WM8400, ID is %x
  ",
  			reg);
1d9f9f040   Mark Brown   mfd: Core support...
66
67
  		return -ENODEV;
  	}
879eed682   Mark Brown   mfd: Remove wm840...
68
  	ret = regmap_read(wm8400->regmap, WM8400_ID, &reg);
1d9f9f040   Mark Brown   mfd: Core support...
69
70
71
72
73
74
75
76
  	if (ret != 0) {
  		dev_err(wm8400->dev, "ID register read failed: %d
  ", ret);
  		return ret;
  	}
  	reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
  	dev_info(wm8400->dev, "WM8400 revision %x
  ", reg);
b8380c1a6   Mark Brown   mfd: Register WM8...
77
78
79
80
  	ret = wm8400_register_codec(wm8400);
  	if (ret != 0) {
  		dev_err(wm8400->dev, "Failed to register codec
  ");
7825dc056   Laxman Dewangan   mfd: wm8400: Use ...
81
  		return ret;
b8380c1a6   Mark Brown   mfd: Register WM8...
82
  	}
1d9f9f040   Mark Brown   mfd: Core support...
83
84
  	if (pdata && pdata->platform_init) {
  		ret = pdata->platform_init(wm8400->dev);
b8380c1a6   Mark Brown   mfd: Register WM8...
85
  		if (ret != 0) {
1d9f9f040   Mark Brown   mfd: Core support...
86
87
88
  			dev_err(wm8400->dev, "Platform init failed: %d
  ",
  				ret);
7825dc056   Laxman Dewangan   mfd: wm8400: Use ...
89
  			return ret;
b8380c1a6   Mark Brown   mfd: Register WM8...
90
  		}
1d9f9f040   Mark Brown   mfd: Core support...
91
92
93
  	} else
  		dev_warn(wm8400->dev, "No platform initialisation supplied
  ");
b8380c1a6   Mark Brown   mfd: Register WM8...
94
  	return 0;
1d9f9f040   Mark Brown   mfd: Core support...
95
  }
50eeef5d3   Mark Brown   mfd: Convert WM84...
96
97
98
99
  static const struct regmap_config wm8400_regmap_config = {
  	.reg_bits = 8,
  	.val_bits = 16,
  	.max_register = WM8400_REGISTER_COUNT - 1,
879eed682   Mark Brown   mfd: Remove wm840...
100
101
102
103
  
  	.volatile_reg = wm8400_volatile,
  
  	.cache_type = REGCACHE_RBTREE,
50eeef5d3   Mark Brown   mfd: Convert WM84...
104
  };
1d9f9f040   Mark Brown   mfd: Core support...
105

879eed682   Mark Brown   mfd: Remove wm840...
106
107
108
109
110
111
112
113
114
  /**
   * wm8400_reset_codec_reg_cache - Reset cached codec registers to
   * their default values.
   */
  void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
  {
  	regmap_reinit_cache(wm8400->regmap, &wm8400_regmap_config);
  }
  EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
5eb519f3f   Javier Martinez Canillas   mfd: Use IS_ENABL...
115
  #if IS_ENABLED(CONFIG_I2C)
1d9f9f040   Mark Brown   mfd: Core support...
116
117
118
119
  static int wm8400_i2c_probe(struct i2c_client *i2c,
  			    const struct i2c_device_id *id)
  {
  	struct wm8400 *wm8400;
1d9f9f040   Mark Brown   mfd: Core support...
120

f57723457   Mark Brown   mfd: Convert WM84...
121
  	wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
82ae61c4b   Sachin Kamat   mfd: wm8400-core:...
122
123
  	if (!wm8400)
  		return -ENOMEM;
1d9f9f040   Mark Brown   mfd: Core support...
124

2b40e9d97   Mark Brown   mfd: wm8400: Conv...
125
  	wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
82ae61c4b   Sachin Kamat   mfd: wm8400-core:...
126
127
  	if (IS_ERR(wm8400->regmap))
  		return PTR_ERR(wm8400->regmap);
50eeef5d3   Mark Brown   mfd: Convert WM84...
128

1d9f9f040   Mark Brown   mfd: Core support...
129
130
  	wm8400->dev = &i2c->dev;
  	i2c_set_clientdata(i2c, wm8400);
82ae61c4b   Sachin Kamat   mfd: wm8400-core:...
131
  	return wm8400_init(wm8400, dev_get_platdata(&i2c->dev));
1d9f9f040   Mark Brown   mfd: Core support...
132
  }
1d9f9f040   Mark Brown   mfd: Core support...
133
134
135
136
  static const struct i2c_device_id wm8400_i2c_id[] = {
         { "wm8400", 0 },
         { }
  };
1d9f9f040   Mark Brown   mfd: Core support...
137
138
139
140
  
  static struct i2c_driver wm8400_i2c_driver = {
  	.driver = {
  		.name = "WM8400",
1d9f9f040   Mark Brown   mfd: Core support...
141
142
  	},
  	.probe    = wm8400_i2c_probe,
1d9f9f040   Mark Brown   mfd: Core support...
143
144
145
  	.id_table = wm8400_i2c_id,
  };
  #endif
b2b658752   Paul Gortmaker   mfd: wm8400-core:...
146
  static int __init wm8400_driver_init(void)
1d9f9f040   Mark Brown   mfd: Core support...
147
148
  {
  	int ret = -ENODEV;
5eb519f3f   Javier Martinez Canillas   mfd: Use IS_ENABL...
149
  #if IS_ENABLED(CONFIG_I2C)
1d9f9f040   Mark Brown   mfd: Core support...
150
151
152
153
154
155
156
157
  	ret = i2c_add_driver(&wm8400_i2c_driver);
  	if (ret != 0)
  		pr_err("Failed to register I2C driver: %d
  ", ret);
  #endif
  
  	return ret;
  }
b2b658752   Paul Gortmaker   mfd: wm8400-core:...
158
  subsys_initcall(wm8400_driver_init);