brintos

brintos / linux-shallow public Read only

0
0
Text · 16.9 KiB · 94a9f8a Raw
667 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2007-2008 Extreme Engineering Solutions, Inc.4 *5 * Author: Nate Case <ncase@xes-inc.com>6 *7 * LED driver for various PCA955x I2C LED drivers8 *9 * Supported devices:10 *11 *	Device		Description		7-bit slave address12 *	------		-----------		-------------------13 *	PCA9550		2-bit driver		0x60 .. 0x6114 *	PCA9551		8-bit driver		0x60 .. 0x6715 *	PCA9552		16-bit driver		0x60 .. 0x6716 *	PCA9553/01	4-bit driver		0x6217 *	PCA9553/02	4-bit driver		0x6318 *19 * Philips PCA955x LED driver chips follow a register map as shown below:20 *21 *	Control Register		Description22 *	----------------		-----------23 *	0x0				Input register 024 *					..25 *	NUM_INPUT_REGS - 1		Last Input register X26 *27 *	NUM_INPUT_REGS			Frequency prescaler 028 *	NUM_INPUT_REGS + 1		PWM register 029 *	NUM_INPUT_REGS + 2		Frequency prescaler 130 *	NUM_INPUT_REGS + 3		PWM register 131 *32 *	NUM_INPUT_REGS + 4		LED selector 033 *	NUM_INPUT_REGS + 434 *	    + NUM_LED_REGS - 1		Last LED selector35 *36 *  where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many37 *  bits the chip supports.38 */39 40#include <linux/bitops.h>41#include <linux/ctype.h>42#include <linux/delay.h>43#include <linux/err.h>44#include <linux/gpio/driver.h>45#include <linux/i2c.h>46#include <linux/leds.h>47#include <linux/module.h>48#include <linux/of.h>49#include <linux/property.h>50#include <linux/slab.h>51#include <linux/string.h>52 53#include <dt-bindings/leds/leds-pca955x.h>54 55/* LED select registers determine the source that drives LED outputs */56#define PCA955X_LS_LED_ON	0x0	/* Output LOW */57#define PCA955X_LS_LED_OFF	0x1	/* Output HI-Z */58#define PCA955X_LS_BLINK0	0x2	/* Blink at PWM0 rate */59#define PCA955X_LS_BLINK1	0x3	/* Blink at PWM1 rate */60 61#define PCA955X_GPIO_INPUT	LED_OFF62#define PCA955X_GPIO_HIGH	LED_OFF63#define PCA955X_GPIO_LOW	LED_FULL64 65enum pca955x_type {66	pca9550,67	pca9551,68	pca9552,69	ibm_pca9552,70	pca9553,71};72 73struct pca955x_chipdef {74	int			bits;75	u8			slv_addr;	/* 7-bit slave address mask */76	int			slv_addr_shift;	/* Number of bits to ignore */77};78 79static const struct pca955x_chipdef pca955x_chipdefs[] = {80	[pca9550] = {81		.bits		= 2,82		.slv_addr	= /* 110000x */ 0x60,83		.slv_addr_shift	= 1,84	},85	[pca9551] = {86		.bits		= 8,87		.slv_addr	= /* 1100xxx */ 0x60,88		.slv_addr_shift	= 3,89	},90	[pca9552] = {91		.bits		= 16,92		.slv_addr	= /* 1100xxx */ 0x60,93		.slv_addr_shift	= 3,94	},95	[ibm_pca9552] = {96		.bits		= 16,97		.slv_addr	= /* 0110xxx */ 0x30,98		.slv_addr_shift	= 3,99	},100	[pca9553] = {101		.bits		= 4,102		.slv_addr	= /* 110001x */ 0x62,103		.slv_addr_shift	= 1,104	},105};106 107struct pca955x {108	struct mutex lock;109	struct pca955x_led *leds;110	const struct pca955x_chipdef	*chipdef;111	struct i2c_client	*client;112	unsigned long active_pins;113#ifdef CONFIG_LEDS_PCA955X_GPIO114	struct gpio_chip gpio;115#endif116};117 118struct pca955x_led {119	struct pca955x	*pca955x;120	struct led_classdev	led_cdev;121	int			led_num;	/* 0 .. 15 potentially */122	u32			type;123	enum led_default_state	default_state;124	struct fwnode_handle	*fwnode;125};126 127struct pca955x_platform_data {128	struct pca955x_led	*leds;129	int			num_leds;130};131 132/* 8 bits per input register */133static inline int pca95xx_num_input_regs(int bits)134{135	return (bits + 7) / 8;136}137 138/*139 * Return an LED selector register value based on an existing one, with140 * the appropriate 2-bit state value set for the given LED number (0-3).141 */142static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)143{144	return (oldval & (~(0x3 << (led_num << 1)))) |145		((state & 0x3) << (led_num << 1));146}147 148/*149 * Write to frequency prescaler register, used to program the150 * period of the PWM output.  period = (PSCx + 1) / 38151 */152static int pca955x_write_psc(struct i2c_client *client, int n, u8 val)153{154	struct pca955x *pca955x = i2c_get_clientdata(client);155	u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n);156	int ret;157 158	ret = i2c_smbus_write_byte_data(client, cmd, val);159	if (ret < 0)160		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",161			__func__, n, val, ret);162	return ret;163}164 165/*166 * Write to PWM register, which determines the duty cycle of the167 * output.  LED is OFF when the count is less than the value of this168 * register, and ON when it is greater.  If PWMx == 0, LED is always OFF.169 *170 * Duty cycle is (256 - PWMx) / 256171 */172static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val)173{174	struct pca955x *pca955x = i2c_get_clientdata(client);175	u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);176	int ret;177 178	ret = i2c_smbus_write_byte_data(client, cmd, val);179	if (ret < 0)180		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",181			__func__, n, val, ret);182	return ret;183}184 185/*186 * Write to LED selector register, which determines the source that187 * drives the LED output.188 */189static int pca955x_write_ls(struct i2c_client *client, int n, u8 val)190{191	struct pca955x *pca955x = i2c_get_clientdata(client);192	u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;193	int ret;194 195	ret = i2c_smbus_write_byte_data(client, cmd, val);196	if (ret < 0)197		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",198			__func__, n, val, ret);199	return ret;200}201 202/*203 * Read the LED selector register, which determines the source that204 * drives the LED output.205 */206static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val)207{208	struct pca955x *pca955x = i2c_get_clientdata(client);209	u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n;210	int ret;211 212	ret = i2c_smbus_read_byte_data(client, cmd);213	if (ret < 0) {214		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",215			__func__, n, ret);216		return ret;217	}218	*val = (u8)ret;219	return 0;220}221 222static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val)223{224	struct pca955x *pca955x = i2c_get_clientdata(client);225	u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n);226	int ret;227 228	ret = i2c_smbus_read_byte_data(client, cmd);229	if (ret < 0) {230		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",231			__func__, n, ret);232		return ret;233	}234	*val = (u8)ret;235	return 0;236}237 238static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev)239{240	struct pca955x_led *pca955x_led = container_of(led_cdev,241						       struct pca955x_led,242						       led_cdev);243	struct pca955x *pca955x = pca955x_led->pca955x;244	u8 ls, pwm;245	int ret;246 247	ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls);248	if (ret)249		return ret;250 251	ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3;252	switch (ls) {253	case PCA955X_LS_LED_ON:254		ret = LED_FULL;255		break;256	case PCA955X_LS_LED_OFF:257		ret = LED_OFF;258		break;259	case PCA955X_LS_BLINK0:260		ret = LED_HALF;261		break;262	case PCA955X_LS_BLINK1:263		ret = pca955x_read_pwm(pca955x->client, 1, &pwm);264		if (ret)265			return ret;266		ret = 255 - pwm;267		break;268	}269 270	return ret;271}272 273static int pca955x_led_set(struct led_classdev *led_cdev,274			    enum led_brightness value)275{276	struct pca955x_led *pca955x_led;277	struct pca955x *pca955x;278	u8 ls;279	int chip_ls;	/* which LSx to use (0-3 potentially) */280	int ls_led;	/* which set of bits within LSx to use (0-3) */281	int ret;282 283	pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);284	pca955x = pca955x_led->pca955x;285 286	chip_ls = pca955x_led->led_num / 4;287	ls_led = pca955x_led->led_num % 4;288 289	mutex_lock(&pca955x->lock);290 291	ret = pca955x_read_ls(pca955x->client, chip_ls, &ls);292	if (ret)293		goto out;294 295	switch (value) {296	case LED_FULL:297		ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);298		break;299	case LED_OFF:300		ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF);301		break;302	case LED_HALF:303		ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0);304		break;305	default:306		/*307		 * Use PWM1 for all other values.  This has the unwanted308		 * side effect of making all LEDs on the chip share the309		 * same brightness level if set to a value other than310		 * OFF, HALF, or FULL.  But, this is probably better than311		 * just turning off for all other values.312		 */313		ret = pca955x_write_pwm(pca955x->client, 1, 255 - value);314		if (ret)315			goto out;316		ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);317		break;318	}319 320	ret = pca955x_write_ls(pca955x->client, chip_ls, ls);321 322out:323	mutex_unlock(&pca955x->lock);324 325	return ret;326}327 328#ifdef CONFIG_LEDS_PCA955X_GPIO329/*330 * Read the INPUT register, which contains the state of LEDs.331 */332static int pca955x_read_input(struct i2c_client *client, int n, u8 *val)333{334	int ret = i2c_smbus_read_byte_data(client, n);335 336	if (ret < 0) {337		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",338			__func__, n, ret);339		return ret;340	}341	*val = (u8)ret;342	return 0;343 344}345 346static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)347{348	struct pca955x *pca955x = gpiochip_get_data(gc);349 350	return test_and_set_bit(offset, &pca955x->active_pins) ? -EBUSY : 0;351}352 353static void pca955x_gpio_free_pin(struct gpio_chip *gc, unsigned int offset)354{355	struct pca955x *pca955x = gpiochip_get_data(gc);356 357	clear_bit(offset, &pca955x->active_pins);358}359 360static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,361			     int val)362{363	struct pca955x *pca955x = gpiochip_get_data(gc);364	struct pca955x_led *led = &pca955x->leds[offset];365 366	if (val)367		return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_HIGH);368 369	return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_LOW);370}371 372static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,373				   int val)374{375	pca955x_set_value(gc, offset, val);376}377 378static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)379{380	struct pca955x *pca955x = gpiochip_get_data(gc);381	struct pca955x_led *led = &pca955x->leds[offset];382	u8 reg = 0;383 384	/* There is nothing we can do about errors */385	pca955x_read_input(pca955x->client, led->led_num / 8, &reg);386 387	return !!(reg & (1 << (led->led_num % 8)));388}389 390static int pca955x_gpio_direction_input(struct gpio_chip *gc,391					unsigned int offset)392{393	struct pca955x *pca955x = gpiochip_get_data(gc);394	struct pca955x_led *led = &pca955x->leds[offset];395 396	/* To use as input ensure pin is not driven. */397	return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_INPUT);398}399 400static int pca955x_gpio_direction_output(struct gpio_chip *gc,401					 unsigned int offset, int val)402{403	return pca955x_set_value(gc, offset, val);404}405#endif /* CONFIG_LEDS_PCA955X_GPIO */406 407static struct pca955x_platform_data *408pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)409{410	struct pca955x_platform_data *pdata;411	struct pca955x_led *led;412	struct fwnode_handle *child;413	int count;414 415	count = device_get_child_node_count(&client->dev);416	if (count > chip->bits)417		return ERR_PTR(-ENODEV);418 419	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);420	if (!pdata)421		return ERR_PTR(-ENOMEM);422 423	pdata->leds = devm_kcalloc(&client->dev,424				   chip->bits, sizeof(struct pca955x_led),425				   GFP_KERNEL);426	if (!pdata->leds)427		return ERR_PTR(-ENOMEM);428 429	device_for_each_child_node(&client->dev, child) {430		u32 reg;431		int res;432 433		res = fwnode_property_read_u32(child, "reg", &reg);434		if ((res != 0) || (reg >= chip->bits))435			continue;436 437		led = &pdata->leds[reg];438		led->type = PCA955X_TYPE_LED;439		led->fwnode = child;440		led->default_state = led_init_default_state_get(child);441 442		fwnode_property_read_u32(child, "type", &led->type);443	}444 445	pdata->num_leds = chip->bits;446 447	return pdata;448}449 450static int pca955x_probe(struct i2c_client *client)451{452	struct pca955x *pca955x;453	struct pca955x_led *pca955x_led;454	const struct pca955x_chipdef *chip;455	struct led_classdev *led;456	struct led_init_data init_data;457	struct i2c_adapter *adapter;458	int i, err;459	struct pca955x_platform_data *pdata;460	bool set_default_label = false;461	bool keep_pwm = false;462	char default_label[8];463 464	chip = i2c_get_match_data(client);465	if (!chip)466		return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n");467 468	adapter = client->adapter;469	pdata = dev_get_platdata(&client->dev);470	if (!pdata) {471		pdata =	pca955x_get_pdata(client, chip);472		if (IS_ERR(pdata))473			return PTR_ERR(pdata);474	}475 476	/* Make sure the slave address / chip type combo given is possible */477	if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=478	    chip->slv_addr) {479		dev_err(&client->dev, "invalid slave address %02x\n",480			client->addr);481		return -ENODEV;482	}483 484	dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at "485		 "slave address 0x%02x\n", client->name, chip->bits,486		 client->addr);487 488	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))489		return -EIO;490 491	if (pdata->num_leds != chip->bits) {492		dev_err(&client->dev,493			"board info claims %d LEDs on a %d-bit chip\n",494			pdata->num_leds, chip->bits);495		return -ENODEV;496	}497 498	pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);499	if (!pca955x)500		return -ENOMEM;501 502	pca955x->leds = devm_kcalloc(&client->dev, chip->bits,503				     sizeof(*pca955x_led), GFP_KERNEL);504	if (!pca955x->leds)505		return -ENOMEM;506 507	i2c_set_clientdata(client, pca955x);508 509	mutex_init(&pca955x->lock);510	pca955x->client = client;511	pca955x->chipdef = chip;512 513	init_data.devname_mandatory = false;514	init_data.devicename = "pca955x";515 516	for (i = 0; i < chip->bits; i++) {517		pca955x_led = &pca955x->leds[i];518		pca955x_led->led_num = i;519		pca955x_led->pca955x = pca955x;520		pca955x_led->type = pdata->leds[i].type;521 522		switch (pca955x_led->type) {523		case PCA955X_TYPE_NONE:524		case PCA955X_TYPE_GPIO:525			break;526		case PCA955X_TYPE_LED:527			led = &pca955x_led->led_cdev;528			led->brightness_set_blocking = pca955x_led_set;529			led->brightness_get = pca955x_led_get;530 531			if (pdata->leds[i].default_state == LEDS_DEFSTATE_OFF) {532				err = pca955x_led_set(led, LED_OFF);533				if (err)534					return err;535			} else if (pdata->leds[i].default_state == LEDS_DEFSTATE_ON) {536				err = pca955x_led_set(led, LED_FULL);537				if (err)538					return err;539			}540 541			init_data.fwnode = pdata->leds[i].fwnode;542 543			if (is_of_node(init_data.fwnode)) {544				if (to_of_node(init_data.fwnode)->name[0] ==545				    '\0')546					set_default_label = true;547				else548					set_default_label = false;549			} else {550				set_default_label = true;551			}552 553			if (set_default_label) {554				snprintf(default_label, sizeof(default_label),555					 "%d", i);556				init_data.default_label = default_label;557			} else {558				init_data.default_label = NULL;559			}560 561			err = devm_led_classdev_register_ext(&client->dev, led,562							     &init_data);563			if (err)564				return err;565 566			set_bit(i, &pca955x->active_pins);567 568			/*569			 * For default-state == "keep", let the core update the570			 * brightness from the hardware, then check the571			 * brightness to see if it's using PWM1. If so, PWM1572			 * should not be written below.573			 */574			if (pdata->leds[i].default_state == LEDS_DEFSTATE_KEEP) {575				if (led->brightness != LED_FULL &&576				    led->brightness != LED_OFF &&577				    led->brightness != LED_HALF)578					keep_pwm = true;579			}580		}581	}582 583	/* PWM0 is used for half brightness or 50% duty cycle */584	err = pca955x_write_pwm(client, 0, 255 - LED_HALF);585	if (err)586		return err;587 588	if (!keep_pwm) {589		/* PWM1 is used for variable brightness, default to OFF */590		err = pca955x_write_pwm(client, 1, 0);591		if (err)592			return err;593	}594 595	/* Set to fast frequency so we do not see flashing */596	err = pca955x_write_psc(client, 0, 0);597	if (err)598		return err;599	err = pca955x_write_psc(client, 1, 0);600	if (err)601		return err;602 603#ifdef CONFIG_LEDS_PCA955X_GPIO604	pca955x->gpio.label = "gpio-pca955x";605	pca955x->gpio.direction_input = pca955x_gpio_direction_input;606	pca955x->gpio.direction_output = pca955x_gpio_direction_output;607	pca955x->gpio.set = pca955x_gpio_set_value;608	pca955x->gpio.get = pca955x_gpio_get_value;609	pca955x->gpio.request = pca955x_gpio_request_pin;610	pca955x->gpio.free = pca955x_gpio_free_pin;611	pca955x->gpio.can_sleep = 1;612	pca955x->gpio.base = -1;613	pca955x->gpio.ngpio = chip->bits;614	pca955x->gpio.parent = &client->dev;615	pca955x->gpio.owner = THIS_MODULE;616 617	err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,618				     pca955x);619	if (err) {620		/* Use data->gpio.dev as a flag for freeing gpiochip */621		pca955x->gpio.parent = NULL;622		dev_warn(&client->dev, "could not add gpiochip\n");623		return err;624	}625	dev_info(&client->dev, "gpios %i...%i\n",626		 pca955x->gpio.base, pca955x->gpio.base +627		 pca955x->gpio.ngpio - 1);628#endif629 630	return 0;631}632 633static const struct i2c_device_id pca955x_id[] = {634	{ "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] },635	{ "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] },636	{ "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] },637	{ "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] },638	{ "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] },639	{}640};641MODULE_DEVICE_TABLE(i2c, pca955x_id);642 643static const struct of_device_id of_pca955x_match[] = {644	{ .compatible = "nxp,pca9550", .data = &pca955x_chipdefs[pca9550] },645	{ .compatible = "nxp,pca9551", .data = &pca955x_chipdefs[pca9551] },646	{ .compatible = "nxp,pca9552", .data = &pca955x_chipdefs[pca9552] },647	{ .compatible = "ibm,pca9552", .data = &pca955x_chipdefs[ibm_pca9552] },648	{ .compatible = "nxp,pca9553", .data = &pca955x_chipdefs[pca9553] },649	{}650};651MODULE_DEVICE_TABLE(of, of_pca955x_match);652 653static struct i2c_driver pca955x_driver = {654	.driver = {655		.name	= "leds-pca955x",656		.of_match_table = of_pca955x_match,657	},658	.probe = pca955x_probe,659	.id_table = pca955x_id,660};661 662module_i2c_driver(pca955x_driver);663 664MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>");665MODULE_DESCRIPTION("PCA955x LED driver");666MODULE_LICENSE("GPL v2");667