File Index
Files and directories
Sign in
FILE

leds-is31fl32xx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/leds/leds-is31fl32xx.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Driver for ISSI IS31FL32xx family of I2C LED controllers
  4 *
  5 * Copyright 2015 Allworx Corp.
  6 *
  7 * Datasheets:
  8 *   http://www.issi.com/US/product-analog-fxled-driver.shtml
  9 *   http://www.si-en.com/product.asp?parentid=890
 10 */
 11
 12#include <linux/device.h>
 13#include <linux/i2c.h>
 14#include <linux/kernel.h>
 15#include <linux/leds.h>
 16#include <linux/module.h>
 17#include <linux/of.h>
 18
 19/* Used to indicate a device has no such register */
 20#define IS31FL32XX_REG_NONE 0xFF
 21
 22/* Software Shutdown bit in Shutdown Register */
 23#define IS31FL32XX_SHUTDOWN_SSD_ENABLE  0
 24#define IS31FL32XX_SHUTDOWN_SSD_DISABLE BIT(0)
 25
 26/* IS31FL3216 has a number of unique registers */
 27#define IS31FL3216_CONFIG_REG 0x00
 28#define IS31FL3216_LIGHTING_EFFECT_REG 0x03
 29#define IS31FL3216_CHANNEL_CONFIG_REG 0x04
 30
 31/* Software Shutdown bit in 3216 Config Register */
 32#define IS31FL3216_CONFIG_SSD_ENABLE  BIT(7)
 33#define IS31FL3216_CONFIG_SSD_DISABLE 0
 34
 35#define IS31FL32XX_PWM_FREQUENCY_22KHZ  0x01
 36
 37struct is31fl32xx_priv;
 38struct is31fl32xx_led_data {
 39	struct led_classdev cdev;
 40	u8 channel; /* 1-based, max priv->cdef->channels */
 41	struct is31fl32xx_priv *priv;
 42};
 43
 44struct is31fl32xx_priv {
 45	const struct is31fl32xx_chipdef *cdef;
 46	struct i2c_client *client;
 47	unsigned int num_leds;
 48	struct is31fl32xx_led_data leds[];
 49};
 50
 51/**
 52 * struct is31fl32xx_chipdef - chip-specific attributes
 53 * @channels            : Number of LED channels
 54 * @shutdown_reg        : address of Shutdown register (optional)
 55 * @pwm_update_reg      : address of PWM Update register
 56 * @global_control_reg  : address of Global Control register (optional)
 57 * @reset_reg           : address of Reset register (optional)
 58 * @output_frequency_setting_reg: address of output frequency register (optional)
 59 * @pwm_register_base   : address of first PWM register
 60 * @pwm_registers_reversed: : true if PWM registers count down instead of up
 61 * @led_control_register_base : address of first LED control register (optional)
 62 * @enable_bits_per_led_control_register: number of LEDs enable bits in each
 63 * @reset_func          : pointer to reset function
 64 * @sw_shutdown_func    : pointer to software shutdown function
 65 *
 66 * For all optional register addresses, the sentinel value %IS31FL32XX_REG_NONE
 67 * indicates that this chip has no such register.
 68 *
 69 * If non-NULL, @reset_func will be called during probing to set all
 70 * necessary registers to a known initialization state. This is needed
 71 * for chips that do not have a @reset_reg.
 72 *
 73 * @enable_bits_per_led_control_register must be >=1 if
 74 * @led_control_register_base != %IS31FL32XX_REG_NONE.
 75 */
 76struct is31fl32xx_chipdef {
 77	u8	channels;
 78	u8	shutdown_reg;
 79	u8	pwm_update_reg;
 80	u8	global_control_reg;
 81	u8	reset_reg;
 82	u8	output_frequency_setting_reg;
 83	u8	pwm_register_base;
 84	bool	pwm_registers_reversed;
 85	u8	led_control_register_base;
 86	u8	enable_bits_per_led_control_register;
 87	int (*reset_func)(struct is31fl32xx_priv *priv);
 88	int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable);
 89};
 90
 91static const struct is31fl32xx_chipdef is31fl3236_cdef = {
 92	.channels				= 36,
 93	.shutdown_reg				= 0x00,
 94	.pwm_update_reg				= 0x25,
 95	.global_control_reg			= 0x4a,
 96	.reset_reg				= 0x4f,
 97	.output_frequency_setting_reg		= IS31FL32XX_REG_NONE,
 98	.pwm_register_base			= 0x01,
 99	.led_control_register_base		= 0x26,
100	.enable_bits_per_led_control_register	= 1,
101};
102
103static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
104	.channels				= 36,
105	.shutdown_reg				= 0x00,
106	.pwm_update_reg				= 0x25,
107	.global_control_reg			= 0x4a,
108	.reset_reg				= 0x4f,
109	.output_frequency_setting_reg		= 0x4b,
110	.pwm_register_base			= 0x01,
111	.led_control_register_base		= 0x26,
112	.enable_bits_per_led_control_register	= 1,
113};
114
115static const struct is31fl32xx_chipdef is31fl3235_cdef = {
116	.channels				= 28,
117	.shutdown_reg				= 0x00,
118	.pwm_update_reg				= 0x25,
119	.global_control_reg			= 0x4a,
120	.reset_reg				= 0x4f,
121	.output_frequency_setting_reg		= IS31FL32XX_REG_NONE,
122	.pwm_register_base			= 0x05,
123	.led_control_register_base		= 0x2a,
124	.enable_bits_per_led_control_register	= 1,
125};
126
127static const struct is31fl32xx_chipdef is31fl3218_cdef = {
128	.channels				= 18,
129	.shutdown_reg				= 0x00,
130	.pwm_update_reg				= 0x16,
131	.global_control_reg			= IS31FL32XX_REG_NONE,
132	.reset_reg				= 0x17,
133	.output_frequency_setting_reg		= IS31FL32XX_REG_NONE,
134	.pwm_register_base			= 0x01,
135	.led_control_register_base		= 0x13,
136	.enable_bits_per_led_control_register	= 6,
137};
138
139static int is31fl3216_reset(struct is31fl32xx_priv *priv);
140static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
141					bool enable);
142static const struct is31fl32xx_chipdef is31fl3216_cdef = {
143	.channels				= 16,
144	.shutdown_reg				= IS31FL32XX_REG_NONE,
145	.pwm_update_reg				= 0xB0,
146	.global_control_reg			= IS31FL32XX_REG_NONE,
147	.reset_reg				= IS31FL32XX_REG_NONE,
148	.output_frequency_setting_reg		= IS31FL32XX_REG_NONE,
149	.pwm_register_base			= 0x10,
150	.pwm_registers_reversed			= true,
151	.led_control_register_base		= 0x01,
152	.enable_bits_per_led_control_register	= 8,
153	.reset_func				= is31fl3216_reset,
154	.sw_shutdown_func			= is31fl3216_software_shutdown,
155};
156
157static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val)
158{
159	int ret;
160
161	dev_dbg(&priv->client->dev, "writing register 0x%02X=0x%02X", reg, val);
162
163	ret =  i2c_smbus_write_byte_data(priv->client, reg, val);
164	if (ret) {
165		dev_err(&priv->client->dev,
166			"register write to 0x%02X failed (error %d)",
167			reg, ret);
168	}
169	return ret;
170}
171
172/*
173 * Custom reset function for IS31FL3216 because it does not have a RESET
174 * register the way that the other IS31FL32xx chips do. We don't bother
175 * writing the GPIO and animation registers, because the registers we
176 * do write ensure those will have no effect.
177 */
178static int is31fl3216_reset(struct is31fl32xx_priv *priv)
179{
180	unsigned int i;
181	int ret;
182
183	ret = is31fl32xx_write(priv, IS31FL3216_CONFIG_REG,
184			       IS31FL3216_CONFIG_SSD_ENABLE);
185	if (ret)
186		return ret;
187	for (i = 0; i < priv->cdef->channels; i++) {
188		ret = is31fl32xx_write(priv, priv->cdef->pwm_register_base+i,
189				       0x00);
190		if (ret)
191			return ret;
192	}
193	ret = is31fl32xx_write(priv, priv->cdef->pwm_update_reg, 0);
194	if (ret)
195		return ret;
196	ret = is31fl32xx_write(priv, IS31FL3216_LIGHTING_EFFECT_REG, 0x00);
197	if (ret)
198		return ret;
199	ret = is31fl32xx_write(priv, IS31FL3216_CHANNEL_CONFIG_REG, 0x00);
200	if (ret)
201		return ret;
202
203	return 0;
204}
205
206/*
207 * Custom Software-Shutdown function for IS31FL3216 because it does not have
208 * a SHUTDOWN register the way that the other IS31FL32xx chips do.
209 * We don't bother doing a read/modify/write on the CONFIG register because
210 * we only ever use a value of '0' for the other fields in that register.
211 */
212static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
213					bool enable)
214{
215	u8 value = enable ? IS31FL3216_CONFIG_SSD_ENABLE :
216			    IS31FL3216_CONFIG_SSD_DISABLE;
217
218	return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value);
219}
220
221/*
222 * NOTE: A mutex is not needed in this function because:
223 * - All referenced data is read-only after probe()
224 * - The I2C core has a mutex on to protect the bus
225 * - There are no read/modify/write operations
226 * - Intervening operations between the write of the PWM register
227 *   and the Update register are harmless.
228 *
229 * Example:
230 *	PWM_REG_1 write 16
231 *	UPDATE_REG write 0
232 *	PWM_REG_2 write 128
233 *	UPDATE_REG write 0
234 *   vs:
235 *	PWM_REG_1 write 16
236 *	PWM_REG_2 write 128
237 *	UPDATE_REG write 0
238 *	UPDATE_REG write 0
239 * are equivalent. Poking the Update register merely applies all PWM
240 * register writes up to that point.
241 */
242static int is31fl32xx_brightness_set(struct led_classdev *led_cdev,
243				     enum led_brightness brightness)
244{
245	const struct is31fl32xx_led_data *led_data =
246		container_of(led_cdev, struct is31fl32xx_led_data, cdev);
247	const struct is31fl32xx_chipdef *cdef = led_data->priv->cdef;
248	u8 pwm_register_offset;
249	int ret;
250
251	dev_dbg(led_cdev->dev, "%s: %d\n", __func__, brightness);
252
253	/* NOTE: led_data->channel is 1-based */
254	if (cdef->pwm_registers_reversed)
255		pwm_register_offset = cdef->channels - led_data->channel;
256	else
257		pwm_register_offset = led_data->channel - 1;
258
259	ret = is31fl32xx_write(led_data->priv,
260			       cdef->pwm_register_base + pwm_register_offset,
261			       brightness);
262	if (ret)
263		return ret;
264
265	return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0);
266}
267
268static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv)
269{
270	const struct is31fl32xx_chipdef *cdef = priv->cdef;
271	int ret;
272
273	if (cdef->reset_reg != IS31FL32XX_REG_NONE) {
274		ret = is31fl32xx_write(priv, cdef->reset_reg, 0);
275		if (ret)
276			return ret;
277	}
278
279	if (cdef->reset_func)
280		return cdef->reset_func(priv);
281
282	return 0;
283}
284
285static int is31fl32xx_software_shutdown(struct is31fl32xx_priv *priv,
286					bool enable)
287{
288	const struct is31fl32xx_chipdef *cdef = priv->cdef;
289	int ret;
290
291	if (cdef->shutdown_reg != IS31FL32XX_REG_NONE) {
292		u8 value = enable ? IS31FL32XX_SHUTDOWN_SSD_ENABLE :
293				    IS31FL32XX_SHUTDOWN_SSD_DISABLE;
294		ret = is31fl32xx_write(priv, cdef->shutdown_reg, value);
295		if (ret)
296			return ret;
297	}
298
299	if (cdef->sw_shutdown_func)
300		return cdef->sw_shutdown_func(priv, enable);
301
302	return 0;
303}
304
305static int is31fl32xx_init_regs(struct is31fl32xx_priv *priv)
306{
307	const struct is31fl32xx_chipdef *cdef = priv->cdef;
308	int ret;
309
310	ret = is31fl32xx_reset_regs(priv);
311	if (ret)
312		return ret;
313
314	/*
315	 * Set enable bit for all channels.
316	 * We will control state with PWM registers alone.
317	 */
318	if (cdef->led_control_register_base != IS31FL32XX_REG_NONE) {
319		u8 value =
320		    GENMASK(cdef->enable_bits_per_led_control_register-1, 0);
321		u8 num_regs = cdef->channels /
322				cdef->enable_bits_per_led_control_register;
323		int i;
324
325		for (i = 0; i < num_regs; i++) {
326			ret = is31fl32xx_write(priv,
327					       cdef->led_control_register_base+i,
328					       value);
329			if (ret)
330				return ret;
331		}
332	}
333
334	ret = is31fl32xx_software_shutdown(priv, false);
335	if (ret)
336		return ret;
337
338	if (cdef->global_control_reg != IS31FL32XX_REG_NONE) {
339		ret = is31fl32xx_write(priv, cdef->global_control_reg, 0x00);
340		if (ret)
341			return ret;
342	}
343
344	return 0;
345}
346
347static int is31fl32xx_parse_child_dt(const struct device *dev,
348				     const struct device_node *child,
349				     struct is31fl32xx_led_data *led_data)
350{
351	struct led_classdev *cdev = &led_data->cdev;
352	int ret = 0;
353	u32 reg;
354
355	ret = of_property_read_u32(child, "reg", &reg);
356	if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
357		dev_err(dev,
358			"Child node %pOF does not have a valid reg property\n",
359			child);
360		return -EINVAL;
361	}
362	led_data->channel = reg;
363
364	cdev->brightness_set_blocking = is31fl32xx_brightness_set;
365
366	return 0;
367}
368
369static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
370					struct is31fl32xx_priv *priv,
371					u8 channel)
372{
373	size_t i;
374
375	for (i = 0; i < priv->num_leds; i++) {
376		if (priv->leds[i].channel == channel)
377			return &priv->leds[i];
378	}
379
380	return NULL;
381}
382
383static int is31fl32xx_parse_dt(struct device *dev,
384			       struct is31fl32xx_priv *priv)
385{
386	const struct is31fl32xx_chipdef *cdef = priv->cdef;
387	int ret = 0;
388
389	if ((cdef->output_frequency_setting_reg != IS31FL32XX_REG_NONE) &&
390	    of_property_read_bool(dev_of_node(dev), "issi,22khz-pwm")) {
391
392		ret = is31fl32xx_write(priv, cdef->output_frequency_setting_reg,
393				       IS31FL32XX_PWM_FREQUENCY_22KHZ);
394
395		if (ret) {
396			dev_err(dev, "Failed to write output PWM frequency register\n");
397			return ret;
398		}
399	}
400
401	for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
402		struct led_init_data init_data = {};
403		struct is31fl32xx_led_data *led_data =
404			&priv->leds[priv->num_leds];
405		const struct is31fl32xx_led_data *other_led_data;
406
407		led_data->priv = priv;
408
409		ret = is31fl32xx_parse_child_dt(dev, child, led_data);
410		if (ret)
411			return ret;
412
413		/* Detect if channel is already in use by another child */
414		other_led_data = is31fl32xx_find_led_data(priv,
415							  led_data->channel);
416		if (other_led_data) {
417			dev_err(dev,
418				"Node %pOF 'reg' conflicts with another LED\n",
419				child);
420			return -EINVAL;
421		}
422
423		init_data.fwnode = of_fwnode_handle(child);
424
425		ret = devm_led_classdev_register_ext(dev, &led_data->cdev,
426						     &init_data);
427		if (ret) {
428			dev_err(dev, "Failed to register LED for %pOF: %d\n",
429				child, ret);
430			return ret;
431		}
432
433		priv->num_leds++;
434	}
435
436	return 0;
437}
438
439static const struct of_device_id of_is31fl32xx_match[] = {
440	{ .compatible = "issi,is31fl3236",  .data = &is31fl3236_cdef, },
441	{ .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, },
442	{ .compatible = "issi,is31fl3235",  .data = &is31fl3235_cdef, },
443	{ .compatible = "issi,is31fl3218",  .data = &is31fl3218_cdef, },
444	{ .compatible = "si-en,sn3218",     .data = &is31fl3218_cdef, },
445	{ .compatible = "issi,is31fl3216",  .data = &is31fl3216_cdef, },
446	{ .compatible = "si-en,sn3216",     .data = &is31fl3216_cdef, },
447	{},
448};
449
450MODULE_DEVICE_TABLE(of, of_is31fl32xx_match);
451
452static int is31fl32xx_probe(struct i2c_client *client)
453{
454	const struct is31fl32xx_chipdef *cdef;
455	struct device *dev = &client->dev;
456	struct is31fl32xx_priv *priv;
457	int count;
458	int ret = 0;
459
460	cdef = device_get_match_data(dev);
461
462	count = of_get_available_child_count(dev_of_node(dev));
463	if (!count)
464		return -EINVAL;
465
466	priv = devm_kzalloc(dev, struct_size(priv, leds, count),
467			    GFP_KERNEL);
468	if (!priv)
469		return -ENOMEM;
470
471	priv->client = client;
472	priv->cdef = cdef;
473	i2c_set_clientdata(client, priv);
474
475	ret = is31fl32xx_init_regs(priv);
476	if (ret)
477		return ret;
478
479	ret = is31fl32xx_parse_dt(dev, priv);
480	if (ret)
481		return ret;
482
483	return 0;
484}
485
486static void is31fl32xx_remove(struct i2c_client *client)
487{
488	struct is31fl32xx_priv *priv = i2c_get_clientdata(client);
489	int ret;
490
491	ret = is31fl32xx_reset_regs(priv);
492	if (ret)
493		dev_err(&client->dev, "Failed to reset registers on removal (%pe)\n",
494			ERR_PTR(ret));
495}
496
497/*
498 * i2c-core (and modalias) requires that id_table be properly filled,
499 * even though it is not used for DeviceTree based instantiation.
500 */
501static const struct i2c_device_id is31fl32xx_id[] = {
502	{ "is31fl3236" },
503	{ "is31fl3236a" },
504	{ "is31fl3235" },
505	{ "is31fl3218" },
506	{ "sn3218" },
507	{ "is31fl3216" },
508	{ "sn3216" },
509	{},
510};
511
512MODULE_DEVICE_TABLE(i2c, is31fl32xx_id);
513
514static struct i2c_driver is31fl32xx_driver = {
515	.driver = {
516		.name	= "is31fl32xx",
517		.of_match_table = of_is31fl32xx_match,
518	},
519	.probe		= is31fl32xx_probe,
520	.remove		= is31fl32xx_remove,
521	.id_table	= is31fl32xx_id,
522};
523
524module_i2c_driver(is31fl32xx_driver);
525
526MODULE_AUTHOR("David Rivshin <drivshin@allworx.com>");
527MODULE_DESCRIPTION("ISSI IS31FL32xx LED driver");
528MODULE_LICENSE("GPL v2");
~/ Read-only file preview.