/* Look for a LED mode with the requested timer frequency. */
ret = netxbig_led_get_timer_mode(&mode, *delay_on, *delay_off,
led_dat->timer, led_dat->num_timer); if (ret < 0) return ret;
mode_val = led_dat->mode_val[mode]; if (mode_val == NETXBIG_LED_INVALID_MODE) return -EINVAL;
gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val);
led_dat->mode = mode; /* * Note that the brightness register is shared between all the * SATA LEDs. So, change the brightness setting for a single * SATA LED will affect all the others.
*/ if (set_brightness)
gpio_ext_set_value(led_dat->gpio_ext,
led_dat->bright_addr, value);
staticint create_netxbig_led(struct platform_device *pdev, struct netxbig_led_platform_data *pdata, struct netxbig_led_data *led_dat, conststruct netxbig_led *template)
{
spin_lock_init(&led_dat->lock);
led_dat->gpio_ext = pdata->gpio_ext;
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->cdev.blink_set = netxbig_led_blink_set;
led_dat->cdev.brightness_set = netxbig_led_set; /* * Because the GPIO extension bus don't allow to read registers * value, there is no way to probe the LED initial state. * So, the initial sysfs LED value for the "brightness" and "sata" * attributes are inconsistent. * * Note that the initial LED state can't be reconfigured. * The reason is that the LED behaviour must stay uniform during * the whole boot process (bootloader+linux).
*/
led_dat->sata = 0;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.max_brightness = template->bright_max;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
led_dat->mode_addr = template->mode_addr;
led_dat->mode_val = template->mode_val;
led_dat->bright_addr = template->bright_addr;
led_dat->timer = pdata->timer;
led_dat->num_timer = pdata->num_timer; /* * If available, expose the SATA activity blink capability through * a "sata" sysfs attribute.
*/ if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE)
led_dat->cdev.groups = netxbig_led_groups;
/** * netxbig_gpio_ext_remove() - Clean up GPIO extension data * @data: managed resource data to clean up * * Since we pick GPIO descriptors from another device than the device our * driver is probing to, we need to register a specific callback to free * these up using managed resources.
*/ staticvoid netxbig_gpio_ext_remove(void *data)
{ struct netxbig_gpio_ext *gpio_ext = data; int i;
for (i = 0; i < gpio_ext->num_addr; i++)
gpiod_put(gpio_ext->addr[i]); for (i = 0; i < gpio_ext->num_data; i++)
gpiod_put(gpio_ext->data[i]);
gpiod_put(gpio_ext->enable);
}
/** * netxbig_gpio_ext_get() - Obtain GPIO extension device data * @dev: main LED device * @gpio_ext_dev: the GPIO extension device * @gpio_ext: the data structure holding the GPIO extension data * * This function walks the subdevice that only contain GPIO line * handles in the device tree and obtains the GPIO descriptors from that * device.
*/ staticint netxbig_gpio_ext_get(struct device *dev, struct device *gpio_ext_dev, struct netxbig_gpio_ext *gpio_ext)
{ struct gpio_desc **addr, **data; int num_addr, num_data; struct gpio_desc *gpiod; int ret; int i;
ret = gpiod_count(gpio_ext_dev, "addr"); if (ret < 0) {
dev_err(dev, "Failed to count GPIOs in DT property addr-gpios\n"); return ret;
}
num_addr = ret;
addr = devm_kcalloc(dev, num_addr, sizeof(*addr), GFP_KERNEL); if (!addr) return -ENOMEM;
/* * We cannot use devm_ managed resources with these GPIO descriptors * since they are associated with the "GPIO extension device" which * does not probe any driver. The device tree parser will however * populate a platform device for it so we can anyway obtain the * GPIO descriptors from the device.
*/ for (i = 0; i < num_addr; i++) {
gpiod = gpiod_get_index(gpio_ext_dev, "addr", i,
GPIOD_OUT_LOW); if (IS_ERR(gpiod)) return PTR_ERR(gpiod);
gpiod_set_consumer_name(gpiod, "GPIO extension addr");
addr[i] = gpiod;
}
gpio_ext->addr = addr;
gpio_ext->num_addr = num_addr;
ret = gpiod_count(gpio_ext_dev, "data"); if (ret < 0) {
dev_err(dev, "Failed to count GPIOs in DT property data-gpios\n"); return ret;
}
num_data = ret;
data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
for (i = 0; i < num_data; i++) {
gpiod = gpiod_get_index(gpio_ext_dev, "data", i,
GPIOD_OUT_LOW); if (IS_ERR(gpiod)) return PTR_ERR(gpiod);
gpiod_set_consumer_name(gpiod, "GPIO extension data");
data[i] = gpiod;
}
gpio_ext->data = data;
gpio_ext->num_data = num_data;
gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(gpiod)) {
dev_err(dev, "Failed to get GPIO from DT property enable-gpio\n"); return PTR_ERR(gpiod);
}
gpiod_set_consumer_name(gpiod, "GPIO extension enable");
gpio_ext->enable = gpiod;
/* GPIO extension */
gpio_ext_np = of_parse_phandle(np, "gpio-ext", 0); if (!gpio_ext_np) {
dev_err(dev, "Failed to get DT handle gpio-ext\n"); return -EINVAL;
}
gpio_ext_pdev = of_find_device_by_node(gpio_ext_np); if (!gpio_ext_pdev) {
of_node_put(gpio_ext_np);
dev_err(dev, "Failed to find platform device for gpio-ext\n"); return -ENODEV;
}
gpio_ext_dev = &gpio_ext_pdev->dev;
gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL); if (!gpio_ext) {
of_node_put(gpio_ext_np);
ret = -ENOMEM; goto put_device;
}
ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext);
of_node_put(gpio_ext_np); if (ret) goto put_device;
pdata->gpio_ext = gpio_ext;
/* Timers (optional) */
ret = of_property_count_u32_elems(np, "timers"); if (ret > 0) { if (ret % 3) {
ret = -EINVAL; goto put_device;
}
num_timers = ret / 3;
timers = devm_kcalloc(dev, num_timers, sizeof(*timers),
GFP_KERNEL); if (!timers) {
ret = -ENOMEM; goto put_device;
} for (i = 0; i < num_timers; i++) {
u32 tmp;
of_property_read_u32_index(np, "timers", 3 * i,
&timers[i].mode); if (timers[i].mode >= NETXBIG_LED_MODE_NUM) {
ret = -EINVAL; goto put_device;
}
of_property_read_u32_index(np, "timers",
3 * i + 1, &tmp);
timers[i].delay_on = tmp;
of_property_read_u32_index(np, "timers",
3 * i + 2, &tmp);
timers[i].delay_off = tmp;
}
pdata->timer = timers;
pdata->num_timer = num_timers;
}
/* LEDs */
num_leds = of_get_available_child_count(np); if (!num_leds) {
dev_err(dev, "No LED subnodes found in DT\n");
ret = -ENODEV; goto put_device;
}
leds = devm_kcalloc(dev, num_leds, sizeof(*leds), GFP_KERNEL); if (!leds) {
ret = -ENOMEM; goto put_device;
}
led = leds;
for_each_available_child_of_node_scoped(np, child) { constchar *string; int *mode_val; int num_modes;
ret = of_property_read_u32(child, "mode-addr",
&led->mode_addr); if (ret) goto put_device;
ret = of_property_read_u32(child, "bright-addr",
&led->bright_addr); if (ret) goto put_device;
ret = of_property_read_u32(child, "max-brightness",
&led->bright_max); if (ret) goto put_device;
mode_val =
devm_kcalloc(dev,
NETXBIG_LED_MODE_NUM, sizeof(*mode_val),
GFP_KERNEL); if (!mode_val) {
ret = -ENOMEM; goto put_device;
}
for (i = 0; i < NETXBIG_LED_MODE_NUM; i++)
mode_val[i] = NETXBIG_LED_INVALID_MODE;
ret = of_property_count_u32_elems(child, "mode-val"); if (ret < 0 || ret % 2) {
ret = -EINVAL; goto put_device;
}
num_modes = ret / 2; if (num_modes > NETXBIG_LED_MODE_NUM) {
ret = -EINVAL; goto put_device;
}
for (i = 0; i < num_modes; i++) { int mode; int val;
of_property_read_u32_index(child, "mode-val", 2 * i, &mode);
of_property_read_u32_index(child, "mode-val", 2 * i + 1, &val); if (mode >= NETXBIG_LED_MODE_NUM) {
ret = -EINVAL; goto put_device;
}
mode_val[mode] = val;
}
led->mode_val = mode_val;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.