|
|
@@ -91,7 +91,9 @@ struct sti_pwm_chip {
|
|
|
struct regmap_field *prescale_low;
|
|
|
struct regmap_field *prescale_high;
|
|
|
struct regmap_field *pwm_out_en;
|
|
|
+ struct regmap_field *pwm_cpt_en;
|
|
|
struct regmap_field *pwm_cpt_int_en;
|
|
|
+ struct regmap_field *pwm_cpt_int_stat;
|
|
|
struct pwm_chip chip;
|
|
|
struct pwm_device *cur;
|
|
|
unsigned long configured;
|
|
|
@@ -311,6 +313,76 @@ static const struct pwm_ops sti_pwm_ops = {
|
|
|
.owner = THIS_MODULE,
|
|
|
};
|
|
|
|
|
|
+static irqreturn_t sti_pwm_interrupt(int irq, void *data)
|
|
|
+{
|
|
|
+ struct sti_pwm_chip *pc = data;
|
|
|
+ struct device *dev = pc->dev;
|
|
|
+ struct sti_cpt_ddata *ddata;
|
|
|
+ int devicenum;
|
|
|
+ unsigned int cpt_int_stat;
|
|
|
+ unsigned int reg;
|
|
|
+ int ret = IRQ_NONE;
|
|
|
+
|
|
|
+ ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ while (cpt_int_stat) {
|
|
|
+ devicenum = ffs(cpt_int_stat) - 1;
|
|
|
+
|
|
|
+ ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Capture input:
|
|
|
+ * _______ _______
|
|
|
+ * | | | |
|
|
|
+ * __| |_________________| |________
|
|
|
+ * ^0 ^1 ^2
|
|
|
+ *
|
|
|
+ * Capture start by the first available rising edge
|
|
|
+ * When a capture event occurs, capture value (CPT_VALx)
|
|
|
+ * is stored, index incremented, capture edge changed.
|
|
|
+ *
|
|
|
+ * After the capture, if the index > 1, we have collected
|
|
|
+ * the necessary data so we signal the thread waiting for it
|
|
|
+ * and disable the capture by setting capture edge to none
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+ regmap_read(pc->regmap,
|
|
|
+ PWM_CPT_VAL(devicenum),
|
|
|
+ &ddata->snapshot[ddata->index]);
|
|
|
+
|
|
|
+ switch (ddata->index) {
|
|
|
+ case 0:
|
|
|
+ case 1:
|
|
|
+ regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), ®);
|
|
|
+ reg ^= PWM_CPT_EDGE_MASK;
|
|
|
+ regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg);
|
|
|
+
|
|
|
+ ddata->index++;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ regmap_write(pc->regmap,
|
|
|
+ PWM_CPT_EDGE(devicenum),
|
|
|
+ CPT_EDGE_DISABLED);
|
|
|
+ wake_up(&ddata->wait);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_err(dev, "Internal error\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ cpt_int_stat &= ~BIT_MASK(devicenum);
|
|
|
+
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Just ACK everything */
|
|
|
+ regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
|
|
|
{
|
|
|
struct device *dev = pc->dev;
|
|
|
@@ -351,6 +423,11 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
|
|
|
if (IS_ERR(pc->pwm_cpt_int_en))
|
|
|
return PTR_ERR(pc->pwm_cpt_int_en);
|
|
|
|
|
|
+ pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap,
|
|
|
+ reg_fields[PWM_CPT_INT_STAT]);
|
|
|
+ if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat))
|
|
|
+ return PTR_ERR(pc->pwm_cpt_int_stat);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -367,7 +444,7 @@ static int sti_pwm_probe(struct platform_device *pdev)
|
|
|
struct sti_pwm_chip *pc;
|
|
|
struct resource *res;
|
|
|
unsigned int i;
|
|
|
- int ret;
|
|
|
+ int irq, ret;
|
|
|
|
|
|
pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
|
|
|
if (!pc)
|
|
|
@@ -388,6 +465,19 @@ static int sti_pwm_probe(struct platform_device *pdev)
|
|
|
if (IS_ERR(pc->regmap))
|
|
|
return PTR_ERR(pc->regmap);
|
|
|
|
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
|
+ if (irq < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to obtain IRQ\n");
|
|
|
+ return irq;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
|
|
|
+ pdev->name, pc);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to request IRQ\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Setup PWM data with default values: some values could be replaced
|
|
|
* with specific ones provided from Device Tree.
|