Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/leds/blink/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 20 kB image not shown  

Quelle  leds-lgm-sso.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel Lightning Mountain SoC LED Serial Shift Output Controller driver
 *
 * Copyright (c) 2020 Intel Corporation.
 */


#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/uaccess.h>

#define SSO_DEV_NAME   "lgm-sso"

#define LED_BLINK_H8_0   0x0
#define LED_BLINK_H8_1   0x4
#define GET_FREQ_OFFSET(pin, src) (((pin) * 6) + ((src) * 2))
#define GET_SRC_OFFSET(pinc)  (((pin) * 6) + 4)

#define DUTY_CYCLE(x)   (0x8 + ((x) * 4))
#define SSO_CON0   0x2B0
#define SSO_CON0_RZFL   BIT(26)
#define SSO_CON0_BLINK_R  BIT(30)
#define SSO_CON0_SWU   BIT(31)

#define SSO_CON1   0x2B4
#define SSO_CON1_FCDSC   GENMASK(21, 20) /* Fixed Divider Shift Clock */
#define SSO_CON1_FPID   GENMASK(24, 23)
#define SSO_CON1_GPTD   GENMASK(26, 25)
#define SSO_CON1_US   GENMASK(31, 30)

#define SSO_CPU    0x2B8
#define SSO_CON2   0x2C4
#define SSO_CON3   0x2C8

/* Driver MACRO */
#define MAX_PIN_NUM_PER_BANK  SZ_32
#define MAX_GROUP_NUM   SZ_4
#define PINS_PER_GROUP   SZ_8
#define FPID_FREQ_RANK_MAX  SZ_4
#define SSO_LED_MAX_NUM   SZ_32
#define MAX_FREQ_RANK   10
#define DEF_GPTC_CLK_RATE  200000000
#define SSO_DEF_BRIGHTNESS  LED_HALF
#define DATA_CLK_EDGE   0 /* 0-rising, 1-falling */

static const u32 freq_div_tbl[] = {4000, 2000, 1000, 800};
static const int freq_tbl[] = {2, 4, 8, 10, 50000, 100000, 200000, 250000};
static const int shift_clk_freq_tbl[] = {25000000, 12500000, 6250000, 3125000};

/*
 * Update Source to update the SOUTs
 * SW - Software has to update the SWU bit
 * GPTC - General Purpose timer is used as clock source
 * FPID - Divided FSC clock (FPID) is used as clock source
 */

enum {
 US_SW = 0,
 US_GPTC = 1,
 US_FPID = 2
};

enum {
 MAX_FPID_FREQ_RANK = 5, /* 1 to 4 */
 MAX_GPTC_FREQ_RANK = 9, /* 5 to 8 */
 MAX_GPTC_HS_FREQ_RANK = 10, /* 9 to 10 */
};

enum {
 LED_GRP0_PIN_MAX = 24,
 LED_GRP1_PIN_MAX = 29,
 LED_GRP2_PIN_MAX = 32,
};

enum {
 LED_GRP0_0_23,
 LED_GRP1_24_28,
 LED_GRP2_29_31,
 LED_GROUP_MAX,
};

enum {
 CLK_SRC_FPID = 0,
 CLK_SRC_GPTC = 1,
 CLK_SRC_GPTC_HS = 2,
};

struct sso_led_priv;

struct sso_led_desc {
 const char *name;
 const char *default_trigger;
 unsigned int brightness;
 unsigned int blink_rate;
 unsigned int retain_state_suspended:1;
 unsigned int retain_state_shutdown:1;
 unsigned int panic_indicator:1;
 unsigned int hw_blink:1;
 unsigned int hw_trig:1;
 unsigned int blinking:1;
 int freq_idx;
 u32 pin;
};

struct sso_led {
 struct list_head list;
 struct led_classdev cdev;
 struct gpio_desc *gpiod;
 struct sso_led_desc desc;
 struct sso_led_priv *priv;
};

struct sso_gpio {
 struct gpio_chip chip;
 int shift_clk_freq;
 int edge;
 int freq;
 u32 pins;
 u32 alloc_bitmap;
};

struct sso_led_priv {
 struct regmap *mmap;
 struct device *dev;
 struct platform_device *pdev;
 struct clk_bulk_data clocks[2];
 u32 fpid_clkrate;
 u32 gptc_clkrate;
 u32 freq[MAX_FREQ_RANK];
 struct list_head led_list;
 struct sso_gpio gpio;
};

static int sso_get_blink_rate_idx(struct sso_led_priv *priv, u32 rate)
{
 int i;

 for (i = 0; i < MAX_FREQ_RANK; i++) {
  if (rate <= priv->freq[i])
   return i;
 }

 return -1;
}

static unsigned int sso_led_pin_to_group(u32 pin)
{
 if (pin < LED_GRP0_PIN_MAX)
  return LED_GRP0_0_23;
 else if (pin < LED_GRP1_PIN_MAX)
  return LED_GRP1_24_28;
 else
  return LED_GRP2_29_31;
}

static u32 sso_led_get_freq_src(int freq_idx)
{
 if (freq_idx < MAX_FPID_FREQ_RANK)
  return CLK_SRC_FPID;
 else if (freq_idx < MAX_GPTC_FREQ_RANK)
  return CLK_SRC_GPTC;
 else
  return CLK_SRC_GPTC_HS;
}

static u32 sso_led_pin_blink_off(u32 pin, unsigned int group)
{
 if (group == LED_GRP2_29_31)
  return pin - LED_GRP1_PIN_MAX;
 else if (group == LED_GRP1_24_28)
  return pin - LED_GRP0_PIN_MAX;
 else /* led 0 - 23 in led 32 location */
  return SSO_LED_MAX_NUM - LED_GRP1_PIN_MAX;
}

static struct sso_led
*cdev_to_sso_led_data(struct led_classdev *led_cdev)
{
 return container_of(led_cdev, struct sso_led, cdev);
}

static void sso_led_freq_set(struct sso_led_priv *priv, u32 pin, int freq_idx)
{
 u32 reg, off, freq_src, val_freq;
 u32 low, high, val;
 unsigned int group;

 if (!freq_idx)
  return;

 group = sso_led_pin_to_group(pin);
 freq_src = sso_led_get_freq_src(freq_idx);
 off = sso_led_pin_blink_off(pin, group);

 if (group == LED_GRP0_0_23)
  return;
 else if (group == LED_GRP1_24_28)
  reg = LED_BLINK_H8_0;
 else
  reg = LED_BLINK_H8_1;

 if (freq_src == CLK_SRC_FPID)
  val_freq = freq_idx - 1;
 else if (freq_src == CLK_SRC_GPTC)
  val_freq = freq_idx - MAX_FPID_FREQ_RANK;

 /* set blink rate idx */
 if (freq_src != CLK_SRC_GPTC_HS) {
  low = GET_FREQ_OFFSET(off, freq_src);
  high = low + 2;
  val = val_freq << high;
  regmap_update_bits(priv->mmap, reg, GENMASK(high, low), val);
 }

 /* select clock source */
 low = GET_SRC_OFFSET(off);
 high = low + 2;
 val = freq_src << high;
 regmap_update_bits(priv->mmap, reg, GENMASK(high, low), val);
}

static void sso_led_brightness_set(struct led_classdev *led_cdev,
       enum led_brightness brightness)
{
 struct sso_led_priv *priv;
 struct sso_led_desc *desc;
 struct sso_led *led;
 int val;

 led = cdev_to_sso_led_data(led_cdev);
 priv = led->priv;
 desc = &led->desc;

 desc->brightness = brightness;
 regmap_write(priv->mmap, DUTY_CYCLE(desc->pin), brightness);

 if (brightness == LED_OFF)
  val = 0;
 else
  val = 1;

 /* HW blink off */
 if (desc->hw_blink && !val && desc->blinking) {
  desc->blinking = 0;
  regmap_update_bits(priv->mmap, SSO_CON2, BIT(desc->pin), 0);
 } else if (desc->hw_blink && val && !desc->blinking) {
  desc->blinking = 1;
  regmap_update_bits(priv->mmap, SSO_CON2, BIT(desc->pin),
       1 << desc->pin);
 }

 if (!desc->hw_trig)
  gpiod_set_value(led->gpiod, val);
}

static enum led_brightness sso_led_brightness_get(struct led_classdev *led_cdev)
{
 struct sso_led *led = cdev_to_sso_led_data(led_cdev);

 return (enum led_brightness)led->desc.brightness;
}

static int
delay_to_freq_idx(struct sso_led *led, unsigned long *delay_on,
    unsigned long *delay_off)
{
 struct sso_led_priv *priv = led->priv;
 unsigned long delay;
 int freq_idx;
 u32 freq;

 if (!*delay_on && !*delay_off) {
  *delay_on = *delay_off = (1000 / priv->freq[0]) / 2;
  return 0;
 }

 delay = *delay_on + *delay_off;
 freq = 1000 / delay;

 freq_idx = sso_get_blink_rate_idx(priv, freq);
 if (freq_idx == -1)
  freq_idx = MAX_FREQ_RANK - 1;

 delay = 1000 / priv->freq[freq_idx];
 *delay_on = *delay_off = delay / 2;

 if (!*delay_on)
  *delay_on = *delay_off = 1;

 return freq_idx;
}

static int
sso_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
    unsigned long *delay_off)
{
 struct sso_led_priv *priv;
 struct sso_led *led;
 int freq_idx;

 led = cdev_to_sso_led_data(led_cdev);
 priv = led->priv;
 freq_idx = delay_to_freq_idx(led, delay_on, delay_off);

 sso_led_freq_set(priv, led->desc.pin, freq_idx);
 regmap_update_bits(priv->mmap, SSO_CON2, BIT(led->desc.pin),
      1 << led->desc.pin);
 led->desc.freq_idx = freq_idx;
 led->desc.blink_rate = priv->freq[freq_idx];
 led->desc.blinking = 1;

 return 1;
}

static void sso_led_hw_cfg(struct sso_led_priv *priv, struct sso_led *led)
{
 struct sso_led_desc *desc = &led->desc;

 /* set freq */
 if (desc->hw_blink) {
  sso_led_freq_set(priv, desc->pin, desc->freq_idx);
  regmap_update_bits(priv->mmap, SSO_CON2, BIT(desc->pin),
       1 << desc->pin);
 }

 if (desc->hw_trig)
  regmap_update_bits(priv->mmap, SSO_CON3, BIT(desc->pin),
       1 << desc->pin);

 /* set brightness */
 regmap_write(priv->mmap, DUTY_CYCLE(desc->pin), desc->brightness);

 /* enable output */
 if (!desc->hw_trig && desc->brightness)
  gpiod_set_value(led->gpiod, 1);
}

static int sso_create_led(struct sso_led_priv *priv, struct sso_led *led,
     struct fwnode_handle *child)
{
 struct sso_led_desc *desc = &led->desc;
 struct led_init_data init_data;
 int err;

 init_data.fwnode = child;
 init_data.devicename = SSO_DEV_NAME;
 init_data.default_label = ":";

 led->cdev.default_trigger = desc->default_trigger;
 led->cdev.brightness_set = sso_led_brightness_set;
 led->cdev.brightness_get = sso_led_brightness_get;
 led->cdev.brightness = desc->brightness;
 led->cdev.max_brightness = LED_FULL;

 if (desc->retain_state_shutdown)
  led->cdev.flags |= LED_RETAIN_AT_SHUTDOWN;
 if (desc->retain_state_suspended)
  led->cdev.flags |= LED_CORE_SUSPENDRESUME;
 if (desc->panic_indicator)
  led->cdev.flags |= LED_PANIC_INDICATOR;

 if (desc->hw_blink)
  led->cdev.blink_set = sso_led_blink_set;

 sso_led_hw_cfg(priv, led);

 err = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data);
 if (err)
  return err;

 list_add(&led->list, &priv->led_list);

 return 0;
}

static void sso_init_freq(struct sso_led_priv *priv)
{
 int i;

 priv->freq[0] = 0;
 for (i = 1; i < MAX_FREQ_RANK; i++) {
  if (i < MAX_FPID_FREQ_RANK) {
   priv->freq[i] = priv->fpid_clkrate / freq_div_tbl[i - 1];
  } else if (i < MAX_GPTC_FREQ_RANK) {
   priv->freq[i] = priv->gptc_clkrate /
    freq_div_tbl[i - MAX_FPID_FREQ_RANK];
  } else if (i < MAX_GPTC_HS_FREQ_RANK) {
   priv->freq[i] = priv->gptc_clkrate;
  }
 }
}

static int sso_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
 struct sso_led_priv *priv = gpiochip_get_data(chip);

 if (priv->gpio.alloc_bitmap & BIT(offset))
  return -EINVAL;

 priv->gpio.alloc_bitmap |= BIT(offset);
 regmap_write(priv->mmap, DUTY_CYCLE(offset), 0xFF);

 return 0;
}

static void sso_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
 struct sso_led_priv *priv = gpiochip_get_data(chip);

 priv->gpio.alloc_bitmap &= ~BIT(offset);
 regmap_write(priv->mmap, DUTY_CYCLE(offset), 0x0);
}

static int sso_gpio_get_dir(struct gpio_chip *chip, unsigned int offset)
{
 return GPIO_LINE_DIRECTION_OUT;
}

static int
sso_gpio_dir_out(struct gpio_chip *chip, unsigned int offset, int value)
{
 struct sso_led_priv *priv = gpiochip_get_data(chip);
 bool bit = !!value;

 regmap_update_bits(priv->mmap, SSO_CPU, BIT(offset), bit << offset);
 if (!priv->gpio.freq)
  regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_SWU,
       SSO_CON0_SWU);

 return 0;
}

static int sso_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
 struct sso_led_priv *priv = gpiochip_get_data(chip);
 u32 reg_val;

 regmap_read(priv->mmap, SSO_CPU, ®_val);

 return !!(reg_val & BIT(offset));
}

static int sso_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
 struct sso_led_priv *priv = gpiochip_get_data(chip);

 regmap_update_bits(priv->mmap, SSO_CPU, BIT(offset), value << offset);
 if (!priv->gpio.freq)
  regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_SWU,
       SSO_CON0_SWU);

 return 0;
}

static int sso_gpio_gc_init(struct device *dev, struct sso_led_priv *priv)
{
 struct gpio_chip *gc = &priv->gpio.chip;

 gc->request             = sso_gpio_request;
 gc->free                = sso_gpio_free;
 gc->get_direction       = sso_gpio_get_dir;
 gc->direction_output    = sso_gpio_dir_out;
 gc->get                 = sso_gpio_get;
 gc->set                 = sso_gpio_set;

 gc->label               = "lgm-sso";
 gc->base                = -1;
 /* To exclude pins from control, use "gpio-reserved-ranges" */
 gc->ngpio               = priv->gpio.pins;
 gc->parent              = dev;
 gc->owner               = THIS_MODULE;

 return devm_gpiochip_add_data(dev, gc, priv);
}

static int sso_gpio_get_freq_idx(int freq)
{
 int idx;

 for (idx = 0; idx < ARRAY_SIZE(freq_tbl); idx++) {
  if (freq <= freq_tbl[idx])
   return idx;
 }

 return -1;
}

static void sso_register_shift_clk(struct sso_led_priv *priv)
{
 int idx, size = ARRAY_SIZE(shift_clk_freq_tbl);
 u32 val = 0;

 for (idx = 0; idx < size; idx++) {
  if (shift_clk_freq_tbl[idx] <= priv->gpio.shift_clk_freq) {
   val = idx;
   break;
  }
 }

 if (idx == size)
  dev_warn(priv->dev, "%s: Invalid freq %d\n",
    __func__, priv->gpio.shift_clk_freq);

 regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_FCDSC,
      FIELD_PREP(SSO_CON1_FCDSC, val));
}

static int sso_gpio_freq_set(struct sso_led_priv *priv)
{
 int freq_idx;
 u32 val;

 freq_idx = sso_gpio_get_freq_idx(priv->gpio.freq);
 if (freq_idx == -1)
  freq_idx = ARRAY_SIZE(freq_tbl) - 1;

 val = freq_idx % FPID_FREQ_RANK_MAX;

 if (!priv->gpio.freq) {
  regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_BLINK_R, 0);
  regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_US,
       FIELD_PREP(SSO_CON1_US, US_SW));
 } else if (freq_idx < FPID_FREQ_RANK_MAX) {
  regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_BLINK_R,
       SSO_CON0_BLINK_R);
  regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_US,
       FIELD_PREP(SSO_CON1_US, US_FPID));
  regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_FPID,
       FIELD_PREP(SSO_CON1_FPID, val));
 } else {
  regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_BLINK_R,
       SSO_CON0_BLINK_R);
  regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_US,
       FIELD_PREP(SSO_CON1_US, US_GPTC));
  regmap_update_bits(priv->mmap, SSO_CON1, SSO_CON1_GPTD,
       FIELD_PREP(SSO_CON1_GPTD, val));
 }

 return 0;
}

static int sso_gpio_hw_init(struct sso_led_priv *priv)
{
 u32 activate;
 int i, err;

 /* Clear all duty cycles */
 for (i = 0; i < priv->gpio.pins; i++) {
  err = regmap_write(priv->mmap, DUTY_CYCLE(i), 0);
  if (err)
   return err;
 }

 /* 4 groups for total 32 pins */
 for (i = 1; i <= MAX_GROUP_NUM; i++) {
  activate = !!(i * PINS_PER_GROUP <= priv->gpio.pins ||
         priv->gpio.pins > (i - 1) * PINS_PER_GROUP);
  err = regmap_update_bits(priv->mmap, SSO_CON1, BIT(i - 1),
      activate << (i - 1));
  if (err)
   return err;
 }

 /* NO HW directly controlled pin by default */
 err = regmap_write(priv->mmap, SSO_CON3, 0);
 if (err)
  return err;

 /* NO BLINK for all pins */
 err = regmap_write(priv->mmap, SSO_CON2, 0);
 if (err)
  return err;

 /* OUTPUT 0 by default */
 err = regmap_write(priv->mmap, SSO_CPU, 0);
 if (err)
  return err;

 /* update edge */
 err = regmap_update_bits(priv->mmap, SSO_CON0, SSO_CON0_RZFL,
     FIELD_PREP(SSO_CON0_RZFL, priv->gpio.edge));
 if (err)
  return err;

 /* Set GPIO update rate */
 sso_gpio_freq_set(priv);

 /* Register shift clock */
 sso_register_shift_clk(priv);

 return 0;
}

static void sso_led_shutdown(struct sso_led *led)
{
 struct sso_led_priv *priv = led->priv;

 /* unregister led */
 devm_led_classdev_unregister(priv->dev, &led->cdev);

 /* clear HW control bit */
 if (led->desc.hw_trig)
  regmap_update_bits(priv->mmap, SSO_CON3, BIT(led->desc.pin), 0);

 led->priv = NULL;
}

static int
__sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled)
{
 struct fwnode_handle *fwnode_child;
 struct device *dev = priv->dev;
 struct sso_led_desc *desc;
 struct sso_led *led;
 const char *tmp;
 u32 prop;
 int ret;

 fwnode_for_each_child_node(fw_ssoled, fwnode_child) {
  led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  if (!led) {
   ret = -ENOMEM;
   goto __dt_err;
  }

  INIT_LIST_HEAD(&led->list);
  led->priv = priv;
  desc = &led->desc;

  led->gpiod = devm_fwnode_gpiod_get(dev, fwnode_child, NULL,
         GPIOD_ASIS, NULL);
  if (IS_ERR(led->gpiod)) {
   ret = dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n");
   goto __dt_err;
  }

  fwnode_property_read_string(fwnode_child,
         "linux,default-trigger",
         &desc->default_trigger);

  if (fwnode_property_present(fwnode_child,
         "retain-state-suspended"))
   desc->retain_state_suspended = 1;

  if (fwnode_property_present(fwnode_child,
         "retain-state-shutdown"))
   desc->retain_state_shutdown = 1;

  if (fwnode_property_present(fwnode_child, "panic-indicator"))
   desc->panic_indicator = 1;

  ret = fwnode_property_read_u32(fwnode_child, "reg", &prop);
  if (ret)
   goto __dt_err;
  if (prop >= SSO_LED_MAX_NUM) {
   dev_err(dev, "invalid LED pin:%u\n", prop);
   ret = -EINVAL;
   goto __dt_err;
  }
  desc->pin = prop;

  if (fwnode_property_present(fwnode_child, "intel,sso-hw-blink"))
   desc->hw_blink = 1;

  desc->hw_trig = fwnode_property_read_bool(fwnode_child,
         "intel,sso-hw-trigger");
  if (desc->hw_trig) {
   desc->default_trigger = NULL;
   desc->retain_state_shutdown = 0;
   desc->retain_state_suspended = 0;
   desc->panic_indicator = 0;
   desc->hw_blink = 0;
  }

  if (fwnode_property_read_u32(fwnode_child,
          "intel,sso-blink-rate-hz", &prop)) {
   /* default first freq rate */
   desc->freq_idx = 0;
   desc->blink_rate = priv->freq[desc->freq_idx];
  } else {
   desc->freq_idx = sso_get_blink_rate_idx(priv, prop);
   if (desc->freq_idx == -1)
    desc->freq_idx = MAX_FREQ_RANK - 1;

   desc->blink_rate = priv->freq[desc->freq_idx];
  }

  if (!fwnode_property_read_string(fwnode_child, "default-state", &tmp)) {
   if (!strcmp(tmp, "on"))
    desc->brightness = LED_FULL;
  }

  ret = sso_create_led(priv, led, fwnode_child);
  if (ret)
   goto __dt_err;
 }

 return 0;

__dt_err:
 fwnode_handle_put(fwnode_child);
 /* unregister leds */
 list_for_each_entry(led, &priv->led_list, list)
  sso_led_shutdown(led);

 return ret;
}

static int sso_led_dt_parse(struct sso_led_priv *priv)
{
 struct fwnode_handle *fwnode = dev_fwnode(priv->dev);
 struct fwnode_handle *fw_ssoled;
 struct device *dev = priv->dev;
 int count;
 int ret;

 count = device_get_child_node_count(dev);
 if (!count)
  return 0;

 fw_ssoled = fwnode_get_named_child_node(fwnode, "ssoled");
 if (fw_ssoled) {
  ret = __sso_led_dt_parse(priv, fw_ssoled);
  fwnode_handle_put(fw_ssoled);
  if (ret)
   return ret;
 }

 return 0;
}

static int sso_probe_gpios(struct sso_led_priv *priv)
{
 struct device *dev = priv->dev;
 int ret;

 if (device_property_read_u32(dev, "ngpios", &priv->gpio.pins))
  priv->gpio.pins = MAX_PIN_NUM_PER_BANK;

 if (priv->gpio.pins > MAX_PIN_NUM_PER_BANK)
  return -EINVAL;

 if (device_property_read_u32(dev, "intel,sso-update-rate-hz",
         &priv->gpio.freq))
  priv->gpio.freq = 0;

 priv->gpio.edge = DATA_CLK_EDGE;
 priv->gpio.shift_clk_freq = -1;

 ret = sso_gpio_hw_init(priv);
 if (ret)
  return ret;

 return sso_gpio_gc_init(dev, priv);
}

static void sso_clock_disable_unprepare(void *data)
{
 struct sso_led_priv *priv = data;

 clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
}

static int intel_sso_led_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 struct sso_led_priv *priv;
 int ret;

 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;

 priv->pdev = pdev;
 priv->dev = dev;

 /* gate clock */
 priv->clocks[0].id = "sso";

 /* fpid clock */
 priv->clocks[1].id = "fpid";

 ret = devm_clk_bulk_get(dev, ARRAY_SIZE(priv->clocks), priv->clocks);
 if (ret) {
  dev_err(dev, "Getting clocks failed!\n");
  return ret;
 }

 ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks);
 if (ret) {
  dev_err(dev, "Failed to prepare and enable clocks!\n");
  return ret;
 }

 ret = devm_add_action_or_reset(dev, sso_clock_disable_unprepare, priv);
 if (ret)
  return ret;

 priv->fpid_clkrate = clk_get_rate(priv->clocks[1].clk);

 priv->mmap = syscon_node_to_regmap(dev->of_node);

 priv->mmap = syscon_node_to_regmap(dev->of_node);
 if (IS_ERR(priv->mmap)) {
  dev_err(dev, "Failed to map iomem!\n");
  return PTR_ERR(priv->mmap);
 }

 ret = sso_probe_gpios(priv);
 if (ret) {
  regmap_exit(priv->mmap);
  return ret;
 }

 INIT_LIST_HEAD(&priv->led_list);

 platform_set_drvdata(pdev, priv);
 sso_init_freq(priv);

 priv->gptc_clkrate = DEF_GPTC_CLK_RATE;

 ret = sso_led_dt_parse(priv);
 if (ret) {
  regmap_exit(priv->mmap);
  return ret;
 }
 dev_info(priv->dev, "sso LED init success!\n");

 return 0;
}

static void intel_sso_led_remove(struct platform_device *pdev)
{
 struct sso_led_priv *priv;
 struct sso_led *led, *n;

 priv = platform_get_drvdata(pdev);

 list_for_each_entry_safe(led, n, &priv->led_list, list) {
  list_del(&led->list);
  sso_led_shutdown(led);
 }

 regmap_exit(priv->mmap);
}

static const struct of_device_id of_sso_led_match[] = {
 { .compatible = "intel,lgm-ssoled" },
 {}
};

MODULE_DEVICE_TABLE(of, of_sso_led_match);

static struct platform_driver intel_sso_led_driver = {
 .probe  = intel_sso_led_probe,
 .remove  = intel_sso_led_remove,
 .driver  = {
   .name = "lgm-ssoled",
   .of_match_table = of_sso_led_match,
 },
};

module_platform_driver(intel_sso_led_driver);

MODULE_DESCRIPTION("Intel SSO LED/GPIO driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=97 H=95 G=95

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.