* [PATCH v3 2/5] pinctrl: samsung: Add GPF support for Exynos5433
From: Chanwoo Choi @ 2016-11-03 6:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478155149-28527-1-git-send-email-cw00.choi@samsung.com>
This patch add the support of GPF[1-5] pin of Exynos5433 SoC. The GPFx need
to support the multiple memory map because the registers of GPFx are located
in the different domain.
Cc: Tomasz Figa <tomasz.figa@gmail.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-gpio at vger.kernel.org
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
---
drivers/pinctrl/samsung/pinctrl-exynos.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index d657b52dfdb5..12f7d1eb65bc 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -1339,6 +1339,11 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
EXYNOS_PIN_BANK_EINTW(8, 0x020, "gpa1", 0x04),
EXYNOS_PIN_BANK_EINTW(8, 0x040, "gpa2", 0x08),
EXYNOS_PIN_BANK_EINTW(8, 0x060, "gpa3", 0x0c),
+ EXYNOS_PIN_BANK_EINTW_EXT(8, 0x020, "gpf1", 0x1004, 1),
+ EXYNOS_PIN_BANK_EINTW_EXT(4, 0x040, "gpf2", 0x1008, 1),
+ EXYNOS_PIN_BANK_EINTW_EXT(4, 0x060, "gpf3", 0x100c, 1),
+ EXYNOS_PIN_BANK_EINTW_EXT(8, 0x080, "gpf4", 0x1010, 1),
+ EXYNOS_PIN_BANK_EINTW_EXT(8, 0x0a0, "gpf5", 0x1014, 1),
};
/* pin banks of exynos5433 pin-controller - AUD */
@@ -1420,6 +1425,7 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
.eint_wkup_init = exynos_eint_wkup_init,
.suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume,
+ .nr_ext_resources = 1,
}, {
/* pin-controller instance 1 data */
.pin_banks = exynos5433_pin_banks1,
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/5] pinctrl: samsung: Add the support the multiple IORESOURCE_MEM for one pin-bank
From: Chanwoo Choi @ 2016-11-03 6:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478155149-28527-1-git-send-email-cw00.choi@samsung.com>
This patch supports the multiple IORESOURCE_MEM resources for one pin-bank.
In the pre-existing Exynos series, the registers of the gpio bank are included
in the one memory map. But, some gpio bank need to support the one more memory
map (IORESOURCE_MEM) because the registers of gpio bank are separated into
the different memory map.
For example,
The both ALIVE and IMEM domain have the different memory base address.
The GFP[1-5] of exynos5433 are composed as following:
- ALIVE domain : WEINT_* registers
- IMEM domain : CON/DAT/PUD/DRV/CONPDN/PUDPDN register
Cc: Tomasz Figa <tomasz.figa@gmail.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-gpio at vger.kernel.org
Suggested-by: Tomasz Figa <tomasz.figa@gmail.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
---
.../bindings/pinctrl/samsung-pinctrl.txt | 19 ++++++++++
drivers/pinctrl/samsung/pinctrl-exynos.c | 39 +++++++++------------
drivers/pinctrl/samsung/pinctrl-exynos.h | 11 ++++++
drivers/pinctrl/samsung/pinctrl-samsung.c | 40 ++++++++++++++--------
drivers/pinctrl/samsung/pinctrl-samsung.h | 10 ++++--
5 files changed, 80 insertions(+), 39 deletions(-)
diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
index d49e22d2a8b5..1baf19eecabf 100644
--- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
@@ -19,11 +19,30 @@ Required Properties:
- "samsung,exynos5260-pinctrl": for Exynos5260 compatible pin-controller.
- "samsung,exynos5410-pinctrl": for Exynos5410 compatible pin-controller.
- "samsung,exynos5420-pinctrl": for Exynos5420 compatible pin-controller.
+ - "samsung,exynos5433-pinctrl": for Exynos5433 compatible pin-controller.
- "samsung,exynos7-pinctrl": for Exynos7 compatible pin-controller.
- reg: Base address of the pin controller hardware module and length of
the address space it occupies.
+ - reg: Second base address of the pin controller if the specific registers
+ of the pin controller are separated into the different base address.
+
+ Eg: GPF[1-5] of Exynos5433 are separated into the two base address.
+ - First base address is for GPAx and GPF[1-5] external interrupt
+ registers.
+ - Second base address is for GPF[1-5] pinctrl registers.
+
+ pinctrl_0: pinctrl at 10580000 {
+ compatible = "samsung,exynos5433-pinctrl";
+ reg = <0x10580000 0x1a20>, <0x11090000 0x100>;
+
+ wakeup-interrupt-controller {
+ compatible = "samsung,exynos7-wakeup-eint";
+ interrupts = <0 16 0>;
+ };
+ };
+
- Pin banks as child nodes: Pin banks of the controller are represented by child
nodes of the controller node. Bank name is taken from name of the node. Each
bank node must contain following properties:
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index d32fa2b5ff82..d657b52dfdb5 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -61,16 +61,15 @@ static void exynos_irq_mask(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
unsigned long mask;
unsigned long flags;
spin_lock_irqsave(&bank->slock, flags);
- mask = readl(d->virt_base + reg_mask);
+ mask = readl(bank->eint_base + reg_mask);
mask |= 1 << irqd->hwirq;
- writel(mask, d->virt_base + reg_mask);
+ writel(mask, bank->eint_base + reg_mask);
spin_unlock_irqrestore(&bank->slock, flags);
}
@@ -80,10 +79,9 @@ static void exynos_irq_ack(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned long reg_pend = our_chip->eint_pend + bank->eint_offset;
- writel(1 << irqd->hwirq, d->virt_base + reg_pend);
+ writel(1 << irqd->hwirq, bank->eint_base + reg_pend);
}
static void exynos_irq_unmask(struct irq_data *irqd)
@@ -91,7 +89,6 @@ static void exynos_irq_unmask(struct irq_data *irqd)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset;
unsigned long mask;
unsigned long flags;
@@ -109,9 +106,9 @@ static void exynos_irq_unmask(struct irq_data *irqd)
spin_lock_irqsave(&bank->slock, flags);
- mask = readl(d->virt_base + reg_mask);
+ mask = readl(bank->eint_base + reg_mask);
mask &= ~(1 << irqd->hwirq);
- writel(mask, d->virt_base + reg_mask);
+ writel(mask, bank->eint_base + reg_mask);
spin_unlock_irqrestore(&bank->slock, flags);
}
@@ -121,7 +118,6 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned int con, trig_type;
unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
@@ -152,10 +148,10 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
else
irq_set_handler_locked(irqd, handle_level_irq);
- con = readl(d->virt_base + reg_con);
+ con = readl(bank->eint_base + reg_con);
con &= ~(EXYNOS_EINT_CON_MASK << shift);
con |= trig_type << shift;
- writel(con, d->virt_base + reg_con);
+ writel(con, bank->eint_base + reg_con);
return 0;
}
@@ -166,7 +162,6 @@ static int exynos_irq_request_resources(struct irq_data *irqd)
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
const struct samsung_pin_bank_type *bank_type = bank->type;
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
unsigned long flags;
@@ -188,10 +183,10 @@ static int exynos_irq_request_resources(struct irq_data *irqd)
spin_lock_irqsave(&bank->slock, flags);
- con = readl(d->virt_base + reg_con);
+ con = readl(bank->eint_base + reg_con);
con &= ~(mask << shift);
con |= EXYNOS_EINT_FUNC << shift;
- writel(con, d->virt_base + reg_con);
+ writel(con, bank->eint_base + reg_con);
spin_unlock_irqrestore(&bank->slock, flags);
@@ -206,7 +201,6 @@ static void exynos_irq_release_resources(struct irq_data *irqd)
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
const struct samsung_pin_bank_type *bank_type = bank->type;
- struct samsung_pinctrl_drv_data *d = bank->drvdata;
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
unsigned long flags;
@@ -221,10 +215,10 @@ static void exynos_irq_release_resources(struct irq_data *irqd)
spin_lock_irqsave(&bank->slock, flags);
- con = readl(d->virt_base + reg_con);
+ con = readl(bank->eint_base + reg_con);
con &= ~(mask << shift);
con |= FUNC_INPUT << shift;
- writel(con, d->virt_base + reg_con);
+ writel(con, bank->eint_base + reg_con);
spin_unlock_irqrestore(&bank->slock, flags);
@@ -274,7 +268,7 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
struct samsung_pin_bank *bank = d->pin_banks;
unsigned int svc, group, pin, virq;
- svc = readl(d->virt_base + EXYNOS_SVC_OFFSET);
+ svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET);
group = EXYNOS_SVC_GROUP(svc);
pin = svc & EXYNOS_SVC_NUM_MASK;
@@ -452,7 +446,6 @@ static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc);
- struct samsung_pinctrl_drv_data *d = eintd->banks[0]->drvdata;
unsigned long pend;
unsigned long mask;
int i;
@@ -461,9 +454,9 @@ static void exynos_irq_demux_eint16_31(struct irq_desc *desc)
for (i = 0; i < eintd->nr_banks; ++i) {
struct samsung_pin_bank *b = eintd->banks[i];
- pend = readl(d->virt_base + b->irq_chip->eint_pend
+ pend = readl(b->eint_base + b->irq_chip->eint_pend
+ b->eint_offset);
- mask = readl(d->virt_base + b->irq_chip->eint_mask
+ mask = readl(b->eint_base + b->irq_chip->eint_mask
+ b->eint_offset);
exynos_irq_demux_eint(pend & ~mask, b->irq_domain);
}
@@ -581,7 +574,7 @@ static void exynos_pinctrl_suspend_bank(
struct samsung_pin_bank *bank)
{
struct exynos_eint_gpio_save *save = bank->soc_priv;
- void __iomem *regs = drvdata->virt_base;
+ void __iomem *regs = bank->eint_base;
save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
+ bank->eint_offset);
@@ -610,7 +603,7 @@ static void exynos_pinctrl_resume_bank(
struct samsung_pin_bank *bank)
{
struct exynos_eint_gpio_save *save = bank->soc_priv;
- void __iomem *regs = drvdata->virt_base;
+ void __iomem *regs = bank->eint_base;
pr_debug("%s: con %#010x => %#010x\n", bank->name,
readl(regs + EXYNOS_GPIO_ECON_OFFSET
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.h b/drivers/pinctrl/samsung/pinctrl-exynos.h
index 0f0f7cedb2dc..5821525a2c84 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.h
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.h
@@ -79,6 +79,17 @@
.name = id \
}
+#define EXYNOS_PIN_BANK_EINTW_EXT(pins, reg, id, offs, pctl_idx) \
+ { \
+ .type = &bank_type_alive, \
+ .pctl_offset = reg, \
+ .nr_pins = pins, \
+ .eint_type = EINT_TYPE_WKUP, \
+ .eint_offset = offs, \
+ .name = id, \
+ .pctl_res_idx = pctl_idx, \
+ } \
+
/**
* struct exynos_weint_data: irq specific data for all the wakeup interrupts
* generated by the external wakeup interrupt controller.
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 620727fabe64..41e62391c33c 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -33,6 +33,9 @@
#include "../core.h"
#include "pinctrl-samsung.h"
+/* maximum number of the memory resources */
+#define SAMSUNG_PINCTRL_NUM_RESOURCES 2
+
/* list of all possible config options supported */
static struct pin_config {
const char *property;
@@ -345,7 +348,7 @@ static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata,
((b->pin_base + b->nr_pins - 1) < pin))
b++;
- *reg = drvdata->virt_base + b->pctl_offset;
+ *reg = b->pctl_base + b->pctl_offset;
*offset = pin - b->pin_base;
if (bank)
*bank = b;
@@ -526,7 +529,7 @@ static void samsung_gpio_set_value(struct gpio_chip *gc,
void __iomem *reg;
u32 data;
- reg = bank->drvdata->virt_base + bank->pctl_offset;
+ reg = bank->pctl_base + bank->pctl_offset;
data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]);
data &= ~(1 << offset);
@@ -554,7 +557,7 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
struct samsung_pin_bank *bank = gpiochip_get_data(gc);
const struct samsung_pin_bank_type *type = bank->type;
- reg = bank->drvdata->virt_base + bank->pctl_offset;
+ reg = bank->pctl_base + bank->pctl_offset;
data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]);
data >>= offset;
@@ -581,8 +584,8 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc,
type = bank->type;
drvdata = bank->drvdata;
- reg = drvdata->virt_base + bank->pctl_offset +
- type->reg_offset[PINCFG_TYPE_FUNC];
+ reg = bank->pctl_base + bank->pctl_offset
+ + type->reg_offset[PINCFG_TYPE_FUNC];
mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
shift = offset * type->fld_width[PINCFG_TYPE_FUNC];
@@ -979,6 +982,8 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
const struct samsung_pin_bank_data *bdata;
const struct samsung_pin_ctrl *ctrl;
struct samsung_pin_bank *bank;
+ struct resource *res;
+ void __iomem *virt_base[SAMSUNG_PINCTRL_NUM_RESOURCES];
int i;
id = of_alias_get_id(node, "pinctrl");
@@ -997,6 +1002,17 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
if (!d->pin_banks)
return ERR_PTR(-ENOMEM);
+ if (ctrl->nr_ext_resources + 1 > SAMSUNG_PINCTRL_NUM_RESOURCES)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < ctrl->nr_ext_resources + 1; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ virt_base[i] = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(virt_base[i]))
+ return ERR_PTR(-EIO);
+ }
+
bank = d->pin_banks;
bdata = ctrl->pin_banks;
for (i = 0; i < ctrl->nr_banks; ++i, ++bdata, ++bank) {
@@ -1013,6 +1029,9 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
bank->drvdata = d;
bank->pin_base = d->nr_pins;
d->nr_pins += bank->nr_pins;
+
+ bank->eint_base = virt_base[0];
+ bank->pctl_base = virt_base[bdata->pctl_res_idx];
}
for_each_child_of_node(node, np) {
@@ -1052,11 +1071,6 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
}
drvdata->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drvdata->virt_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(drvdata->virt_base))
- return PTR_ERR(drvdata->virt_base);
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
drvdata->irq = res->start;
@@ -1094,12 +1108,11 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
static void samsung_pinctrl_suspend_dev(
struct samsung_pinctrl_drv_data *drvdata)
{
- void __iomem *virt_base = drvdata->virt_base;
int i;
for (i = 0; i < drvdata->nr_banks; i++) {
struct samsung_pin_bank *bank = &drvdata->pin_banks[i];
- void __iomem *reg = virt_base + bank->pctl_offset;
+ void __iomem *reg = bank->pctl_base + bank->pctl_offset;
const u8 *offs = bank->type->reg_offset;
const u8 *widths = bank->type->fld_width;
enum pincfg_type type;
@@ -1140,7 +1153,6 @@ static void samsung_pinctrl_suspend_dev(
*/
static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
{
- void __iomem *virt_base = drvdata->virt_base;
int i;
if (drvdata->resume)
@@ -1148,7 +1160,7 @@ static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
for (i = 0; i < drvdata->nr_banks; i++) {
struct samsung_pin_bank *bank = &drvdata->pin_banks[i];
- void __iomem *reg = virt_base + bank->pctl_offset;
+ void __iomem *reg = bank->pctl_base + bank->pctl_offset;
const u8 *offs = bank->type->reg_offset;
const u8 *widths = bank->type->fld_width;
enum pincfg_type type;
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h
index cd31bfaf62cb..043cb6c11180 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.h
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.h
@@ -116,6 +116,7 @@ struct samsung_pin_bank_type {
* struct samsung_pin_bank_data: represent a controller pin-bank (init data).
* @type: type of the bank (register offsets and bitfield widths)
* @pctl_offset: starting offset of the pin-bank registers.
+ * @pctl_res_idx: index of base address for pin-bank registers.
* @nr_pins: number of pins included in this bank.
* @eint_func: function to set in CON register to configure pin as EINT.
* @eint_type: type of the external interrupt supported by the bank.
@@ -126,6 +127,7 @@ struct samsung_pin_bank_type {
struct samsung_pin_bank_data {
const struct samsung_pin_bank_type *type;
u32 pctl_offset;
+ u8 pctl_res_idx;
u8 nr_pins;
u8 eint_func;
enum eint_type eint_type;
@@ -137,8 +139,10 @@ struct samsung_pin_bank_data {
/**
* struct samsung_pin_bank: represent a controller pin-bank.
* @type: type of the bank (register offsets and bitfield widths)
+ * @pctl_base: base address of the pin-bank registers
* @pctl_offset: starting offset of the pin-bank registers.
* @nr_pins: number of pins included in this bank.
+ * @eint_base: base address of the pin-bank EINT registers.
* @eint_func: function to set in CON register to configure pin as EINT.
* @eint_type: type of the external interrupt supported by the bank.
* @eint_mask: bit mask of pins which support EINT function.
@@ -157,8 +161,10 @@ struct samsung_pin_bank_data {
*/
struct samsung_pin_bank {
const struct samsung_pin_bank_type *type;
+ void __iomem *pctl_base;
u32 pctl_offset;
u8 nr_pins;
+ void __iomem *eint_base;
u8 eint_func;
enum eint_type eint_type;
u32 eint_mask;
@@ -182,6 +188,7 @@ struct samsung_pin_bank {
* struct samsung_pin_ctrl: represent a pin controller.
* @pin_banks: list of pin banks included in this controller.
* @nr_banks: number of pin banks.
+ * @nr_ext_resources: number of the extra base address for pin banks.
* @eint_gpio_init: platform specific callback to setup the external gpio
* interrupts for the controller.
* @eint_wkup_init: platform specific callback to setup the external wakeup
@@ -190,6 +197,7 @@ struct samsung_pin_bank {
struct samsung_pin_ctrl {
const struct samsung_pin_bank_data *pin_banks;
u32 nr_banks;
+ int nr_ext_resources;
int (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
int (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
@@ -200,7 +208,6 @@ struct samsung_pin_ctrl {
/**
* struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
* @node: global list node
- * @virt_base: register base address of the controller.
* @dev: device instance representing the controller.
* @irq: interrpt number used by the controller to notify gpio interrupts.
* @ctrl: pin controller instance managed by the driver.
@@ -215,7 +222,6 @@ struct samsung_pin_ctrl {
*/
struct samsung_pinctrl_drv_data {
struct list_head node;
- void __iomem *virt_base;
struct device *dev;
int irq;
--
1.9.1
^ permalink raw reply related
* [PATCH v3 0/5] arm64: dts: Add the dts file for Exynos5433 and TM/TM2E board
From: Chanwoo Choi @ 2016-11-03 6:39 UTC (permalink / raw)
To: linux-arm-kernel
This patchset adds the Device Tree file for Samsung 64-bit Exynos5433 SoC
and TM/TM2E board based on Exynos5433. The Exynos5433 has Octa-core CPUs
(quad Cortex-A57 and quad Cortex-A53). The TM2 and TM2E are the Samsung board
based on Exynos5433 SoC.
I tested the display controller by using the modetest tool.
But, Samsung s6e3ha2 panel driver has not yet posted. So, the TM2 dts file
don't include the panel Device Tree node. I'll post the s6e3ha2 panel driver
on separate patch with a dt patch.
Changes from v2:
(https://lkml.org/lkml/2016/8/24/426)
- Add new pctl_base and eint_base instead of virt_base on patch1.
- Modify the documentation of pinctrl-samsung.txt on patch1.
- Modify the cooling level for big.LITTLE cores.
- Use the GIC_SPI definition on dtsi file.
- Removes the unneeded 'regulator-always-on' property.
- Fix the kbuild report of the compatible string for TM2/TM2E patches.
- Two patches(exynos-mct, exynos5433-pmu) were merged.
- Add the reviewed-by tag of Javier Martinez Canillas on patch3/4/5
- Add the reviewed-by tag of Krzysztof Kozlowski on patch4/5
- Add the acked-by tag of Rob Herring on patch5
- Fix the minor issues.
Changes from v1:
(https://lkml.org/lkml/2016/8/16/61)
- Merge the cpufreq patch for exynos5433[3] on PM/cpufreq tree.
- Add new patch to support the multiple IORESOURCE_MAP for samsung pinctrl driver.
- Drop the SYSMMU Device-Tree node which will be posted by Marek Szyprowski.
- Fix the code clean issue by Krzysztof Kozlowski and Rob Herring's comment.
- Delete the unnecessary alias from exynos5433-tm2.dts.
- Expand the range of cooling level of CPU device.
- Update the binding method of 'Exynos5433 audio subsystem'
Depends on:
This patchset are based on v4.9-rc3.
Chanwoo Choi (5):
pinctrl: samsung: Add the support the multiple IORESOURCE_MEM for one pin-bank
pinctrl: samsung: Add GPF support for Exynos5433
arm64: dts: exynos: Add dts files for Samsung Exynos5433 64bit SoC
arm64: dts: exynos: Add dts file for Exynos5433-based TM2 board
arm64: dts: exynos: Add dts file for Exynos5433-based TM2E board
.../bindings/arm/samsung/samsung-boards.txt | 2 +
.../bindings/pinctrl/samsung-pinctrl.txt | 19 +
arch/arm64/boot/dts/exynos/Makefile | 5 +-
arch/arm64/boot/dts/exynos/exynos5433-pinctrl.dtsi | 794 ++++++++++++
arch/arm64/boot/dts/exynos/exynos5433-tm2.dts | 974 ++++++++++++++
arch/arm64/boot/dts/exynos/exynos5433-tm2e.dts | 41 +
.../dts/exynos/exynos5433-tmu-g3d-sensor-conf.dtsi | 23 +
.../dts/exynos/exynos5433-tmu-sensor-conf.dtsi | 22 +
arch/arm64/boot/dts/exynos/exynos5433-tmu.dtsi | 296 +++++
arch/arm64/boot/dts/exynos/exynos5433.dtsi | 1356 ++++++++++++++++++++
drivers/pinctrl/samsung/pinctrl-exynos.c | 45 +-
drivers/pinctrl/samsung/pinctrl-exynos.h | 11 +
drivers/pinctrl/samsung/pinctrl-samsung.c | 40 +-
drivers/pinctrl/samsung/pinctrl-samsung.h | 10 +-
14 files changed, 3598 insertions(+), 40 deletions(-)
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-pinctrl.dtsi
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-tm2.dts
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-tm2e.dts
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-tmu-g3d-sensor-conf.dtsi
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-tmu-sensor-conf.dtsi
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-tmu.dtsi
create mode 100644 arch/arm64/boot/dts/exynos/exynos5433.dtsi
--
1.9.1
^ permalink raw reply
* [PATCH v2 1/2] PM/devfreq: add suspend frequency support
From: hl @ 2016-11-03 6:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478153770-8433-2-git-send-email-hl@rock-chips.com>
Hi All,
Miss something in this patch, just ignored it, sorry about that.
On 2016?11?03? 14:16, Lin Huang wrote:
> Add suspend frequency support and if needed set it to
> the frequency obtained from the suspend opp (can be defined
> using opp-v2 bindings and is optional).
>
> Change-Id: Iaa0d3848d63d9ce03f65ea76f263e4685a4c295e
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
> Changes in v2:
> - use update_devfreq() instead devfreq_update_status()
>
> drivers/devfreq/devfreq.c | 17 ++++++++++++++++-
> include/linux/devfreq.h | 9 +++++++++
> 2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index bf3ea76..0a76282 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -364,7 +364,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
> return;
> }
>
> - devfreq_update_status(devfreq, devfreq->previous_freq);
> + if (devfreq->suspend_freq)
> + update_devfreq(devfreq, devfreq->suspend_freq);
> + else
> + devfreq_update_status(devfreq, devfreq->previous_freq);
> devfreq->stop_polling = true;
> mutex_unlock(&devfreq->lock);
> cancel_delayed_work_sync(&devfreq->work);
> @@ -1251,6 +1254,18 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
> }
> EXPORT_SYMBOL(devfreq_recommended_opp);
>
> +void devfreq_opp_get_suspend_opp(struct device *dev, struct devfreq *devfreq)
> +{
> + struct dev_pm_opp *suspend_opp;
> +
> + rcu_read_lock();
> + suspend_opp = dev_pm_opp_get_suspend_opp(dev);
> + if (suspend_opp)
> + devfreq->suspend_freq = dev_pm_opp_get_freq(suspend_opp);
> + rcu_read_unlock();
> +}
> +EXPORT_SYMBOL(devfreq_opp_get_suspend_opp);
> +
> /**
> * devfreq_register_opp_notifier() - Helper function to get devfreq notified
> * for any changes in the OPP availability
> diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
> index 2de4e2e..c785ad9 100644
> --- a/include/linux/devfreq.h
> +++ b/include/linux/devfreq.h
> @@ -88,6 +88,7 @@ struct devfreq_dev_status {
> */
> struct devfreq_dev_profile {
> unsigned long initial_freq;
> + unsigned long suspend_freq;
> unsigned int polling_ms;
>
> int (*target)(struct device *dev, unsigned long *freq, u32 flags);
> @@ -172,6 +173,7 @@ struct devfreq {
> struct delayed_work work;
>
> unsigned long previous_freq;
> + unsigned long suspend_freq;
> struct devfreq_dev_status last_status;
>
> void *data; /* private data for governors */
> @@ -214,6 +216,8 @@ extern void devm_devfreq_remove_device(struct device *dev,
> /* Helper functions for devfreq user device driver with OPP. */
> extern struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
> unsigned long *freq, u32 flags);
> +extern void devfreq_opp_get_suspend_opp(struct device *dev,
> + struct devfreq *devfreq);
> extern int devfreq_register_opp_notifier(struct device *dev,
> struct devfreq *devfreq);
> extern int devfreq_unregister_opp_notifier(struct device *dev,
> @@ -348,6 +352,11 @@ static inline struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
> return ERR_PTR(-EINVAL);
> }
>
> +static inline void devfreq_opp_get_suspend_opp(struct device *dev,
> + struct devfreq *devfreq)
> +{
> +}
> +
> static inline int devfreq_register_opp_notifier(struct device *dev,
> struct devfreq *devfreq)
> {
--
Lin Huang
^ permalink raw reply
* [PATCH 13/13] Documentation: synopsys-dw-mshc: remove the unused properties
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
"support-highspeed" was the obsoleted property.
And "broken-cd" is not synopsys specific property.
It can be referred to mmc.txt binding Documentation.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 -----
1 file changed, 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 21c8251..3dd2849 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -68,11 +68,6 @@ Optional properties:
* card-detect-delay: Delay in milli-seconds before detecting card after card
insert event. The default value is 0.
-* supports-highspeed (DEPRECATED): Enables support for high speed cards (up to 50MHz)
- (use "cap-mmc-highspeed" or "cap-sd-highspeed" instead)
-
-* broken-cd: as documented in mmc core bindings.
-
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
specified we'll defer probe until we can find this regulator.
--
2.10.1
^ permalink raw reply related
* [PATCH 12/13] ARM64: dts: rockchip: replace to "max-frequency" instead of "clock-freq-min-max"
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
In drivers/mmc/core/host.c, there is "max-freqeuncy" property.
It should be same behavior, So Use the "max-frequency" instead of
"clock-freq-min-max".
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts | 2 +-
arch/arm64/boot/dts/rockchip/rk3368.dtsi | 6 +++---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
index 5797933..0acf8a2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
@@ -347,7 +347,7 @@
&sdmmc {
bus-width = <4>;
clock-frequency = <50000000>;
- clock-freq-min-max = <400000 50000000>;
+ max-frequency = <50000000>;
cap-sd-highspeed;
card-detect-delay = <200>;
keep-power-in-suspend;
diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index 0fcb214..79e7c88 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -231,7 +231,7 @@
sdmmc: dwmmc at ff0c0000 {
compatible = "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x0 0xff0c0000 0x0 0x4000>;
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
@@ -243,7 +243,7 @@
sdio0: dwmmc at ff0d0000 {
compatible = "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x0 0xff0d0000 0x0 0x4000>;
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>,
<&cru SCLK_SDIO0_DRV>, <&cru SCLK_SDIO0_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
@@ -255,7 +255,7 @@
emmc: dwmmc at ff0f0000 {
compatible = "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x0 0xff0f0000 0x0 0x4000>;
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
<&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index b65c193..b1c011d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -235,7 +235,7 @@
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe310000 0x0 0x4000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>;
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
<&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
@@ -248,7 +248,7 @@
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe320000 0x0 0x4000>;
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>;
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
--
2.10.1
^ permalink raw reply related
* [PATCH 11/13] ARM: dts: rockchip: replace to "max-frequency" instead of "clock-freq-min-max"
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
In drivers/mmc/core/host.c, there is "max-frequency" property.
It should be same behavior. So use the "max-frequency" instead of
"clock-freq-min-max".
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
arch/arm/boot/dts/rk3036.dtsi | 6 +++---
arch/arm/boot/dts/rk322x.dtsi | 2 +-
arch/arm/boot/dts/rk3288.dtsi | 8 ++++----
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index a935523..d3fb323 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -244,7 +244,7 @@
compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x10214000 0x4000>;
clock-frequency = <37500000>;
- clock-freq-min-max = <400000 37500000>;
+ max-frequency = <37500000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
clock-names = "biu", "ciu";
fifo-depth = <0x100>;
@@ -255,7 +255,7 @@
sdio: dwmmc at 10218000 {
compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x10218000 0x4000>;
- clock-freq-min-max = <400000 37500000>;
+ max-frequency = <37500000>;
clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
<&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
@@ -271,7 +271,7 @@
bus-width = <8>;
cap-mmc-highspeed;
clock-frequency = <37500000>;
- clock-freq-min-max = <400000 37500000>;
+ max-frequency = <37500000>;
clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
<&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi
index 9e6bf0e..8c2e360 100644
--- a/arch/arm/boot/dts/rk322x.dtsi
+++ b/arch/arm/boot/dts/rk322x.dtsi
@@ -402,7 +402,7 @@
reg = <0x30020000 0x4000>;
interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <37500000>;
- clock-freq-min-max = <400000 37500000>;
+ max-frequency = <37500000>;
clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
<&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 17ec2e2..65b60b7 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -227,7 +227,7 @@
sdmmc: dwmmc at ff0c0000 {
compatible = "rockchip,rk3288-dw-mshc";
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
@@ -239,7 +239,7 @@
sdio0: dwmmc at ff0d0000 {
compatible = "rockchip,rk3288-dw-mshc";
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>,
<&cru SCLK_SDIO0_DRV>, <&cru SCLK_SDIO0_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
@@ -251,7 +251,7 @@
sdio1: dwmmc at ff0e0000 {
compatible = "rockchip,rk3288-dw-mshc";
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>,
<&cru SCLK_SDIO1_DRV>, <&cru SCLK_SDIO1_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
@@ -263,7 +263,7 @@
emmc: dwmmc at ff0f0000 {
compatible = "rockchip,rk3288-dw-mshc";
- clock-freq-min-max = <400000 150000000>;
+ max-frequency = <150000000>;
clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
<&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
--
2.10.1
^ permalink raw reply related
* [PATCH 10/13] ARM: dts: exynos: replace to "max-frequecy" instead of "clock-freq-min-max"
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
In drivers/mmc/core/host.c, there is "max-frequency" property.
It should be same behavior. So Use the "max-frequency" instead of
"clock-freq-min-max".
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
arch/arm/boot/dts/exynos3250-artik5-eval.dts | 2 +-
arch/arm/boot/dts/exynos3250-artik5.dtsi | 2 +-
arch/arm/boot/dts/exynos3250-monk.dts | 2 +-
arch/arm/boot/dts/exynos3250-rinato.dts | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/exynos3250-artik5-eval.dts b/arch/arm/boot/dts/exynos3250-artik5-eval.dts
index be4d6aa..4bd2ee8 100644
--- a/arch/arm/boot/dts/exynos3250-artik5-eval.dts
+++ b/arch/arm/boot/dts/exynos3250-artik5-eval.dts
@@ -28,7 +28,7 @@
vqmmc-supply = <&ldo3_reg>;
card-detect-delay = <200>;
clock-frequency = <100000000>;
- clock-freq-min-max = <400000 100000000>;
+ max-frequency = <100000000>;
samsung,dw-mshc-ciu-div = <1>;
samsung,dw-mshc-sdr-timing = <0 1>;
samsung,dw-mshc-ddr-timing = <1 2>;
diff --git a/arch/arm/boot/dts/exynos3250-artik5.dtsi b/arch/arm/boot/dts/exynos3250-artik5.dtsi
index a70819b..59c89d7 100644
--- a/arch/arm/boot/dts/exynos3250-artik5.dtsi
+++ b/arch/arm/boot/dts/exynos3250-artik5.dtsi
@@ -310,7 +310,7 @@
card-detect-delay = <200>;
vmmc-supply = <&ldo12_reg>;
clock-frequency = <100000000>;
- clock-freq-min-max = <400000 100000000>;
+ max-frequency = <100000000>;
samsung,dw-mshc-ciu-div = <1>;
samsung,dw-mshc-sdr-timing = <0 1>;
samsung,dw-mshc-ddr-timing = <1 2>;
diff --git a/arch/arm/boot/dts/exynos3250-monk.dts b/arch/arm/boot/dts/exynos3250-monk.dts
index 66f04f6..cccfe4b 100644
--- a/arch/arm/boot/dts/exynos3250-monk.dts
+++ b/arch/arm/boot/dts/exynos3250-monk.dts
@@ -435,7 +435,7 @@
card-detect-delay = <200>;
vmmc-supply = <&vemmc_reg>;
clock-frequency = <100000000>;
- clock-freq-min-max = <400000 100000000>;
+ max-frequency = <100000000>;
samsung,dw-mshc-ciu-div = <1>;
samsung,dw-mshc-sdr-timing = <0 1>;
samsung,dw-mshc-ddr-timing = <1 2>;
diff --git a/arch/arm/boot/dts/exynos3250-rinato.dts b/arch/arm/boot/dts/exynos3250-rinato.dts
index 3967ee5..548413e 100644
--- a/arch/arm/boot/dts/exynos3250-rinato.dts
+++ b/arch/arm/boot/dts/exynos3250-rinato.dts
@@ -649,7 +649,7 @@
card-detect-delay = <200>;
vmmc-supply = <&ldo12_reg>;
clock-frequency = <100000000>;
- clock-freq-min-max = <400000 100000000>;
+ max-frequency = <100000000>;
samsung,dw-mshc-ciu-div = <1>;
samsung,dw-mshc-sdr-timing = <0 1>;
samsung,dw-mshc-ddr-timing = <1 2>;
--
2.10.1
^ permalink raw reply related
* [PATCH 09/13] mmc: dw_mmc: remove the "clock-freq-min-max" property
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
Remove the "clock-freq-min-max" property.
There is "max-frequency" property in drivers/mmc/core/host.c
It can be used for getting maximum frequency.
And minimum clock value is assigned to 100K by default.
Because MMC core should check the initial clock value from
400K to 100K until finding correct value.
It's why Minimum value puts 100K by default.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 3 ---
drivers/mmc/host/dw_mmc.c | 13 ++++---------
2 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 4e00e85..21c8251 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -56,9 +56,6 @@ Optional properties:
is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time.
-* clock-freq-min-max: Minimum and Maximum clock frequency for card output
- clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
-
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index bd8c5ea..a0c5a85 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2589,7 +2589,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct dw_mci_slot *slot;
const struct dw_mci_drv_data *drv_data = host->drv_data;
int ctrl_id, ret;
- u32 freq[2];
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
if (!mmc)
@@ -2603,14 +2602,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
host->slot[id] = slot;
mmc->ops = &dw_mci_ops;
- if (of_property_read_u32_array(host->dev->of_node,
- "clock-freq-min-max", freq, 2)) {
- mmc->f_min = DW_MCI_FREQ_MIN;
- mmc->f_max = DW_MCI_FREQ_MAX;
- } else {
- mmc->f_min = freq[0];
- mmc->f_max = freq[1];
- }
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
@@ -2649,6 +2640,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (ret)
goto err_host_allocated;
+ mmc->f_min = DW_MCI_FREQ_MIN;
+ if (!mmc->f_max)
+ mmc->f_max = DW_MCI_FREQ_MAX;
+
/* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) {
mmc->max_segs = host->ring_size;
--
2.10.1
^ permalink raw reply related
* [PATCH 08/13] mmc: dw_mmc: remove the unnecessary mmc_data structure
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
Remove the unnecessary mmc_data structure.
Instead, cmd->data can be used.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index df151c8..bd8c5ea 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -236,7 +236,6 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{
- struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
u32 cmdr;
@@ -291,10 +290,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
if (cmd->flags & MMC_RSP_CRC)
cmdr |= SDMMC_CMD_RESP_CRC;
- data = cmd->data;
- if (data) {
+ if (cmd->data) {
cmdr |= SDMMC_CMD_DAT_EXP;
- if (data->flags & MMC_DATA_WRITE)
+ if (cmd->data->flags & MMC_DATA_WRITE)
cmdr |= SDMMC_CMD_DAT_WR;
}
--
2.10.1
^ permalink raw reply related
* [PATCH 07/13] mmc: dw_mmc: use the cookie's enum values for post/pre_req()
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
This patch removed the meaningless value. Instead, use the cookie's enum
values for executing correctly.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 39 +++++++++++++++++++--------------------
1 file changed, 19 insertions(+), 20 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 16df93b..df151c8 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -414,12 +414,13 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
{
struct mmc_data *data = host->data;
- if (data)
- if (!data->host_cookie)
- dma_unmap_sg(host->dev,
- data->sg,
- data->sg_len,
- dw_mci_get_dma_dir(data));
+ if (data && data->host_cookie == COOKIE_MAPPED) {
+ dma_unmap_sg(host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data));
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
}
static void dw_mci_idmac_reset(struct dw_mci *host)
@@ -850,13 +851,13 @@ static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
static int dw_mci_pre_dma_transfer(struct dw_mci *host,
struct mmc_data *data,
- bool next)
+ int cookie)
{
struct scatterlist *sg;
unsigned int i, sg_len;
- if (!next && data->host_cookie)
- return data->host_cookie;
+ if (data->host_cookie == COOKIE_PRE_MAPPED)
+ return data->sg_len;
/*
* We don't do DMA on "complex" transfers, i.e. with
@@ -881,8 +882,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
if (sg_len == 0)
return -EINVAL;
- if (next)
- data->host_cookie = sg_len;
+ data->host_cookie = cookie;
return sg_len;
}
@@ -897,13 +897,12 @@ static void dw_mci_pre_req(struct mmc_host *mmc,
if (!slot->host->use_dma || !data)
return;
- if (data->host_cookie) {
- data->host_cookie = 0;
- return;
- }
+ /* This data might be unmapped at this time */
+ data->host_cookie = COOKIE_UNMAPPED;
- if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0)
- data->host_cookie = 0;
+ if (dw_mci_pre_dma_transfer(slot->host, mrq->data,
+ COOKIE_PRE_MAPPED) < 0)
+ data->host_cookie = COOKIE_UNMAPPED;
}
static void dw_mci_post_req(struct mmc_host *mmc,
@@ -916,12 +915,12 @@ static void dw_mci_post_req(struct mmc_host *mmc,
if (!slot->host->use_dma || !data)
return;
- if (data->host_cookie)
+ if (data->host_cookie != COOKIE_UNMAPPED)
dma_unmap_sg(slot->host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
- data->host_cookie = 0;
+ data->host_cookie = COOKIE_UNMAPPED;
}
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
@@ -1027,7 +1026,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
if (!host->use_dma)
return -ENODEV;
- sg_len = dw_mci_pre_dma_transfer(host, data, 0);
+ sg_len = dw_mci_pre_dma_transfer(host, data, COOKIE_MAPPED);
if (sg_len < 0) {
host->dma_ops->stop(host);
return sg_len;
--
2.10.1
^ permalink raw reply related
* [PATCH 06/13] mmc: core: move the cookie's enum values from sdhci.h to mmc.h
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
It's not for only sdhci controller.
So it can be moved from sdhci.h to mmc.h. And renamed from sdhci_cookie
to mmc_cookie.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/sdhci.h | 6 ------
include/linux/mmc/core.h | 6 ++++++
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 766df17..325663b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -321,12 +321,6 @@ struct sdhci_adma2_64_desc {
/* Allow for a a command request and a data request at the same time */
#define SDHCI_MAX_MRQS 2
-enum sdhci_cookie {
- COOKIE_UNMAPPED,
- COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
- COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */
-};
-
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 2b953eb..d23a675 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -106,6 +106,12 @@ struct mmc_command {
struct mmc_request *mrq; /* associated request */
};
+enum mmc_cookie {
+ COOKIE_UNMAPPED,
+ COOKIE_PRE_MAPPED, /* mapped by pre_req() of controller */
+ COOKIE_MAPPED, /* mapped by prepare_data() of controller */
+};
+
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */
unsigned int timeout_clks; /* data timeout (in clocks) */
--
2.10.1
^ permalink raw reply related
* [PATCH 05/13] mmc: dw_mmc: call the dw_mci_prep_stop_abort() by default
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
stop_cmdr should be set to values relevant to stop command.
It migth be assigned to values whatever there is mrq->stop or not.
Then it doesn't need to use dw_mci_prepare_command().
It's enough to use the prep_stop_abort for preparing stop command.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 23fc505..16df93b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -385,7 +385,7 @@ static void dw_mci_start_command(struct dw_mci *host,
static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
{
- struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
+ struct mmc_command *stop = &host->stop_abort;
dw_mci_start_command(host, stop, host->stop_cmdr);
}
@@ -1277,10 +1277,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
- if (mrq->stop)
- host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
- else
- host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
+ host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
}
static void dw_mci_start_request(struct dw_mci *host,
@@ -1890,8 +1887,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- if (data->stop ||
- !(host->data_status & (SDMMC_INT_DRTO |
+ if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
@@ -1927,8 +1923,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- if (data->stop ||
- !(host->data_status & (SDMMC_INT_DRTO |
+ if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
@@ -2004,7 +1999,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->cmd = NULL;
host->data = NULL;
- if (mrq->stop)
+ if (!mrq->sbc && mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
--
2.10.1
^ permalink raw reply related
* [PATCH 04/13] mmc: dw_mmc: use the hold register when send stop command
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
If DW_MMC_CARD_NO_USE_HOLD isn't set, it's usesd by default.
Enve if SDMMC_CMD_USB_HOLD_REG is set in prepare_command(), but it
doesn't set in pre_stop_abort().
To maintain the consistency, add the checking condition for this.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 6ccb817..23fc505 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -337,6 +337,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
cmdr = stop->opcode | SDMMC_CMD_STOP |
SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
+ if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags))
+ cmdr |= SDMMC_CMD_USE_HOLD_REG;
+
return cmdr;
}
--
2.10.1
^ permalink raw reply related
* [PATCH 03/13] mmc: dw_mmc: change the DW_MCI_FREQ_MIN from 400K to 100K
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
If there is no property "clock-freq-min-max", mmc->f_min should be set
to 400K by default. But Some SoC can be used 100K.
When 100K is used, MMC core will try to check from 400K to 100K.
Reported-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1e71fd4..6ccb817 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -54,7 +54,7 @@
#define DW_MCI_DMA_THRESHOLD 16
#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */
-#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */
+#define DW_MCI_FREQ_MIN 100000 /* unit: HZ */
#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
--
2.10.1
^ permalink raw reply related
* [PATCH 02/13] mmc: dw_mmc: fix the debug message for checking card's present
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
If display the debug message, this message should be spamming.
If flags is maintained the previous value, didn't display the debug
message.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index b87dd07..1e71fd4 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1537,13 +1537,10 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
== 0 ? 1 : 0;
spin_lock_bh(&host->lock);
- if (present) {
- set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
dev_dbg(&mmc->class_dev, "card is present\n");
- } else {
- clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ else if (!test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
dev_dbg(&mmc->class_dev, "card is not present\n");
- }
spin_unlock_bh(&host->lock);
return present;
--
2.10.1
^ permalink raw reply related
* [PATCH 01/13] mmc: dw_mmc: display the real register value on debugfs
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161103062135.10697-1-jh80.chung@samsung.com>
Developer wants to see the real register value, not register offset.
This patch fixed to display the real value of register.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/dw_mmc.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 1c9ee36..b87dd07 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -165,12 +165,14 @@ static const struct file_operations dw_mci_req_fops = {
static int dw_mci_regs_show(struct seq_file *s, void *v)
{
- seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
- seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
- seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
- seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
- seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
- seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+ struct dw_mci *host = s->private;
+
+ seq_printf(s, "STATUS:\t0x%08x\n", mci_readl(host, STATUS));
+ seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS));
+ seq_printf(s, "CMD:\t0x%08x\n", mci_readl(host, CMD));
+ seq_printf(s, "CTRL:\t0x%08x\n", mci_readl(host, CTRL));
+ seq_printf(s, "INTMASK:\t0x%08x\n", mci_readl(host, INTMASK));
+ seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA));
return 0;
}
--
2.10.1
^ permalink raw reply related
* [PATCH 00/13] mmc: dw_mmc: cleans the codes for dwmmc controller
From: Jaehoon Chung @ 2016-11-03 6:21 UTC (permalink / raw)
To: linux-arm-kernel
This patchset is modified the some minor fixing and cleaning code.
If needs to split the patches, i will re-send the patches.
* Major changes
- Use the cookie enum values like sdhci controller.
- Remove the unnecessary codes and use stop_abort() by default.
- Remove the obsoleted property "supports-highspeed"
- Remove the "clock-freq-min-max" property. Instead, use "max-frequency"
- Minimum clock value is set to 100K by default.
Jaehoon Chung (13):
mmc: dw_mmc: display the real register value on debugfs
mmc: dw_mmc: fix the debug message for checking card's present
mmc: dw_mmc: change the DW_MCI_FREQ_MIN from 400K to 100K
mmc: dw_mmc: use the hold register when send stop command
mmc: dw_mmc: call the dw_mci_prep_stop_abort() by default
mmc: core: move the cookie's enum values from sdhci.h to mmc.h
mmc: dw_mmc: use the cookie's enum values for post/pre_req()
mmc: dw_mmc: remove the unnecessary mmc_data structure
mmc: dw_mmc: remove the "clock-freq-min-max" property
ARM: dts: exynos: replace to "max-frequecy" instead of
"clock-freq-min-max"
ARM: dts: rockchip: replace to "max-frequency" instead of
"clock-freq-min-max"
ARM64: dts: rockchip: replace to "max-frequency" instead of
"clock-freq-min-max"
Documentation: synopsys-dw-mshc: remove the unused properties
.../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 8 --
arch/arm/boot/dts/exynos3250-artik5-eval.dts | 2 +-
arch/arm/boot/dts/exynos3250-artik5.dtsi | 2 +-
arch/arm/boot/dts/exynos3250-monk.dts | 2 +-
arch/arm/boot/dts/exynos3250-rinato.dts | 2 +-
arch/arm/boot/dts/rk3036.dtsi | 6 +-
arch/arm/boot/dts/rk322x.dtsi | 2 +-
arch/arm/boot/dts/rk3288.dtsi | 8 +-
.../boot/dts/rockchip/rk3368-orion-r68-meta.dts | 2 +-
arch/arm64/boot/dts/rockchip/rk3368.dtsi | 6 +-
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 4 +-
drivers/mmc/host/dw_mmc.c | 99 ++++++++++------------
drivers/mmc/host/sdhci.h | 6 --
include/linux/mmc/core.h | 6 ++
14 files changed, 68 insertions(+), 87 deletions(-)
--
2.10.1
^ permalink raw reply
* [PATCH v2 2/2] PM/devfreq: rk3399: set specific ddr frequency when stop ddr dvfs
From: Lin Huang @ 2016-11-03 6:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478153770-8433-1-git-send-email-hl@rock-chips.com>
We need ddr run a specific frequency when ddr dvfs stop
working. For example: if we enable two monitor, then we will
stop ddr dvfs, but we hope ddr can run in highest frequency
obviously.
Change-Id: I02460f0becbf0e1a732e2a55b8e529f53c01109c
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v2:
- None
drivers/devfreq/rk3399_dmc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c
index e24b73d..9309941 100644
--- a/drivers/devfreq/rk3399_dmc.c
+++ b/drivers/devfreq/rk3399_dmc.c
@@ -443,7 +443,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
if (IS_ERR(data->devfreq))
return PTR_ERR(data->devfreq);
devm_devfreq_register_opp_notifier(dev, data->devfreq);
-
+ devfreq_opp_get_suspend_opp(dev, data->devfreq);
data->dev = dev;
platform_set_drvdata(pdev, data);
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/2] PM/devfreq: add suspend frequency support
From: Lin Huang @ 2016-11-03 6:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478153770-8433-1-git-send-email-hl@rock-chips.com>
Add suspend frequency support and if needed set it to
the frequency obtained from the suspend opp (can be defined
using opp-v2 bindings and is optional).
Change-Id: Iaa0d3848d63d9ce03f65ea76f263e4685a4c295e
Signed-off-by: Lin Huang <hl@rock-chips.com>
---
Changes in v2:
- use update_devfreq() instead devfreq_update_status()
drivers/devfreq/devfreq.c | 17 ++++++++++++++++-
include/linux/devfreq.h | 9 +++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index bf3ea76..0a76282 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -364,7 +364,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
return;
}
- devfreq_update_status(devfreq, devfreq->previous_freq);
+ if (devfreq->suspend_freq)
+ update_devfreq(devfreq, devfreq->suspend_freq);
+ else
+ devfreq_update_status(devfreq, devfreq->previous_freq);
devfreq->stop_polling = true;
mutex_unlock(&devfreq->lock);
cancel_delayed_work_sync(&devfreq->work);
@@ -1251,6 +1254,18 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
}
EXPORT_SYMBOL(devfreq_recommended_opp);
+void devfreq_opp_get_suspend_opp(struct device *dev, struct devfreq *devfreq)
+{
+ struct dev_pm_opp *suspend_opp;
+
+ rcu_read_lock();
+ suspend_opp = dev_pm_opp_get_suspend_opp(dev);
+ if (suspend_opp)
+ devfreq->suspend_freq = dev_pm_opp_get_freq(suspend_opp);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(devfreq_opp_get_suspend_opp);
+
/**
* devfreq_register_opp_notifier() - Helper function to get devfreq notified
* for any changes in the OPP availability
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 2de4e2e..c785ad9 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -88,6 +88,7 @@ struct devfreq_dev_status {
*/
struct devfreq_dev_profile {
unsigned long initial_freq;
+ unsigned long suspend_freq;
unsigned int polling_ms;
int (*target)(struct device *dev, unsigned long *freq, u32 flags);
@@ -172,6 +173,7 @@ struct devfreq {
struct delayed_work work;
unsigned long previous_freq;
+ unsigned long suspend_freq;
struct devfreq_dev_status last_status;
void *data; /* private data for governors */
@@ -214,6 +216,8 @@ extern void devm_devfreq_remove_device(struct device *dev,
/* Helper functions for devfreq user device driver with OPP. */
extern struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
unsigned long *freq, u32 flags);
+extern void devfreq_opp_get_suspend_opp(struct device *dev,
+ struct devfreq *devfreq);
extern int devfreq_register_opp_notifier(struct device *dev,
struct devfreq *devfreq);
extern int devfreq_unregister_opp_notifier(struct device *dev,
@@ -348,6 +352,11 @@ static inline struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
return ERR_PTR(-EINVAL);
}
+static inline void devfreq_opp_get_suspend_opp(struct device *dev,
+ struct devfreq *devfreq)
+{
+}
+
static inline int devfreq_register_opp_notifier(struct device *dev,
struct devfreq *devfreq)
{
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/2] set specific ddr frequency when stop ddr dvfs
From: Lin Huang @ 2016-11-03 6:16 UTC (permalink / raw)
To: linux-arm-kernel
We need ddr run a specific frequency when ddr dvfs stop working. So we
implement get suspend frequency function in devfreq framework, and call
it in rk3399 dmc driver.
Lin Huang (2):
PM/devfreq: add suspend frequency support
PM/devfreq: rk3399: set specific ddr frequency when stop ddr dvfs
drivers/devfreq/devfreq.c | 17 ++++++++++++++++-
drivers/devfreq/rk3399_dmc.c | 2 +-
include/linux/devfreq.h | 9 +++++++++
3 files changed, 26 insertions(+), 2 deletions(-)
--
1.9.1
^ permalink raw reply
* [RESEND PATCH v1 11/11] dts: arm64: hip06: Add Hisilicon SoC PMU support
From: Anurup M @ 2016-11-03 5:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478151727-20250-1-git-send-email-anurup.m@huawei.com>
1. Add nodes for hip06 L3 cache to support uncore events.
2. Add nodes for hip06 MN to support uncore events.
3. Add nodes for hip06 DDRC to support uncore events.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 116 +++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index cb9e018..9ff3afe 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -980,6 +980,122 @@
status = "disabled";
};
+ djtag0: djtag at 60010000 {
+ compatible = "hisilicon,hip06-cpu-djtag-v1";
+ reg = <0x0 0x60010000 0x0 0x10000>;
+
+ /* L3 cache for socket0 CPU die scl#2 */
+ pmul3c0 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x02>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04 0x04 0x04 0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+
+ /* Miscellaneous node for socket0
+ * CPU die scl#2
+ */
+ pmumn0 {
+ compatible = "hisilicon,hisi-pmu-mn-v1";
+ scl-id = <0x02>;
+ num-events = <0x09>;
+ num-counters = <0x04>;
+ module-id = <0x0b>;
+ cfgen-map = <0x01>;
+ counter-reg = <0x30>;
+ evctrl-reg = <0x40>;
+ event-en = <0x01>;
+ evtype-reg = <0x48>;
+ };
+ };
+
+ djtag1: djtag at 40010000 {
+ compatible = "hisilicon,hip06-cpu-djtag-v1";
+ reg = <0x0 0x40010000 0x0 0x10000>;
+
+ /* L3 cache for socket0 CPU die scl#1 */
+ pmul3c1 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x01>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04 0x04 0x04 0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+
+ /* Miscellaneous node for socket0
+ * CPU die scl#1
+ */
+ pmumn1 {
+ compatible = "hisilicon,hisi-pmu-mn-v1";
+ scl-id = <0x01>;
+ num-events = <0x09>;
+ num-counters = <0x04>;
+ module-id = <0x0b>;
+ cfgen-map = <0x01>;
+ counter-reg = <0x30>;
+ evctrl-reg = <0x40>;
+ event-en = <0x01>;
+ evtype-reg = <0x48>;
+ };
+ };
+
+ /* DDRC for CPU die scl #1 Channel #0 */
+ pmu_sccl0_ddrc0: pmu_ddrc0 at 40348000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x01>;
+ ch-id = <0x0>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x40348000 0x0 0x10000>; /* TOTEMA DDRC0 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #1 Channel #1 */
+ pmu_sccl0_ddrc1: pmu_ddrc1 at 40358000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x01>;
+ ch-id = <0x01>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x40358000 0x0 0x10000>; /* TOTEMA DDRC1 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #2 Channel #0 */
+ pmu_sccl1_ddrc0: pmu_ddrc0 at 60348000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x02>;
+ ch-id = <0x0>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x60348000 0x0 0x10000>; /* TOTEMC DDRC0 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #2 Channel #1 */
+ pmu_sccl1_ddrc1: pmu_ddrc1 at 60358000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x02>;
+ ch-id = <0x01>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x60358000 0x0 0x10000>; /* TOTEMC DDRC1 */
+ status = "okay";
+ };
+
sas1: sas at a2000000 {
compatible = "hisilicon,hip06-sas-v2";
reg = <0 0xa2000000 0 0x10000>;
--
2.1.4
^ permalink raw reply related
* [RESEND PATCH v1 10/11] perf: hisi: Support for Hisilicon DDRC PMU.
From: Anurup M @ 2016-11-03 5:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478151727-20250-1-git-send-email-anurup.m@huawei.com>
1. Add support for counting Hisilicon DDRC
statistics events in perf.
2. Support a total of 13 statistics events.
3. Events listed in /sys/devices/<pmu_name>/
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
drivers/perf/hisilicon/Makefile | 2 +-
drivers/perf/hisilicon/hisi_uncore_ddrc.c | 444 ++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_ddrc.h | 73 +++++
3 files changed, 518 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 8975104..8e9df2e 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o hisi_uncore_ddrc.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.c b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
new file mode 100644
index 0000000..b89a72e
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
@@ -0,0 +1,444 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_ddrc.h"
+
+static inline int hisi_ddrc_counter_valid(int idx, struct hisi_pmu *ddrc_pmu)
+{
+ return (idx >= 0 && idx < ddrc_pmu->num_counters);
+}
+
+static u32 hisi_ddrc_read32_relaxed(void __iomem *regs_base, u32 off)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ return readl_relaxed(reg_addr);
+}
+
+static void hisi_ddrc_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ writel(val, reg_addr);
+}
+
+static u32 hisi_read_ddrc_counter(struct hisi_ddrc_data *ddrc_hwmod_data,
+ unsigned long event_code, int idx)
+{
+ u32 value;
+ u32 reg_off;
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+ value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+ reg_off);
+ return value;
+}
+
+static u64 hisi_ddrc_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_ddrc_data *ddrc_hwmod_data;
+ u64 delta, prev_raw_count, new_raw_count = 0;
+
+ if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+ dev_err(ddrc_pmu->dev,
+ "%s: Unsupported event index:%d!\n", __func__, idx);
+ return 0;
+ }
+
+ ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+
+ /* Check if the DDRC data is initialized for this SCCL */
+ if (!ddrc_hwmod_data->regs_base) {
+ dev_err(ddrc_pmu->dev, "DDRC registers not mapped!\n");
+ return 0;
+ }
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count =
+ hisi_read_ddrc_counter(ddrc_hwmod_data,
+ hwc->config_base, idx);
+ delta = (new_raw_count - prev_raw_count) &
+ HISI_MAX_PERIOD;
+
+ local64_add(delta, &event->count);
+ } while (local64_cmpxchg(
+ &hwc->prev_count, prev_raw_count, new_raw_count) !=
+ prev_raw_count);
+
+ return new_raw_count;
+}
+
+static u32 hisi_write_ddrc_counter(struct hisi_pmu *ddrc_pmu,
+ struct hw_perf_event *hwc, u32 value)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ u32 reg_off;
+ u32 event_code = hwc->config_base;
+
+ if (!(event_code >= HISI_HWEVENT_DDRC_FLUX_WR &&
+ event_code < HISI_HWEVENT_DDRC_MAX_EVENT)) {
+ dev_err(ddrc_pmu->dev, "Unknown DDR evevnt!");
+ return 0;
+ }
+
+ if (!ddrc_hwmod_data->regs_base) {
+ dev_err(ddrc_pmu->dev,
+ "DDR reg address not mapped!\n");
+ return 0;
+ }
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+ hisi_ddrc_write32(ddrc_hwmod_data->regs_base,
+ reg_off, value);
+
+ return value;
+}
+
+static void hisi_ddrc_set_event_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ u32 event_code = hwc->config_base;
+ u32 reg_off;
+ u32 value;
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+ /*
+ * For Hisilicon DDRC PMU we save the current counter value
+ * to prev_count, as we have enabled continuous counting for
+ * DDRC.
+ */
+ value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+ reg_off);
+ local64_set(&hwc->prev_count, value);
+}
+
+static void hisi_clear_ddrc_event_idx(struct hisi_pmu *ddrc_pmu, int idx)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ void *bitmap_addr;
+
+ if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+ dev_err(ddrc_pmu->dev,
+ "%s:Unsupported event index:%d!\n", __func__, idx);
+ return;
+ }
+
+ bitmap_addr = ddrc_hwmod_data->hisi_ddrc_event_used_mask;
+
+ __clear_bit(idx, bitmap_addr);
+}
+
+static int hisi_ddrc_get_event_idx(struct hisi_pmu *ddrc_pmu)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ int event_idx;
+
+ event_idx =
+ find_first_zero_bit(
+ ddrc_hwmod_data->hisi_ddrc_event_used_mask,
+ ddrc_pmu->num_counters);
+
+ if (event_idx == ddrc_pmu->num_counters)
+ return -EAGAIN;
+
+ __set_bit(event_idx,
+ ddrc_hwmod_data->hisi_ddrc_event_used_mask);
+
+ return event_idx;
+}
+
+static void hisi_free_ddrc_data(struct hisi_pmu *ddrc_pmu)
+{
+ kfree(ddrc_pmu->hwmod_data);
+ ddrc_pmu->hwmod_data = NULL;
+}
+
+static void init_hisi_ddr(void __iomem *reg_base)
+{
+ u32 value;
+
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 0);
+
+ value = hisi_ddrc_read32_relaxed(reg_base, HISI_DDRC_CFG_PERF_REG_OFF);
+ value &= 0x2fffffff;
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CFG_PERF_REG_OFF, value);
+
+ /* Enable Continuous counting */
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 1);
+}
+
+static int init_hisi_ddrc_dts_data(struct platform_device *pdev,
+ struct hisi_ddrc_data *ddrc_hwmod_data)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ /* Continue for zero entries */
+ if (!res) {
+ dev_err(&pdev->dev, "No DDR reg resorces!\n");
+ return -EINVAL;
+ }
+
+ if (!resource_size(res)) {
+ dev_err(&pdev->dev, "Zero DDR reg entry!\n");
+ return -EINVAL;
+ }
+
+ ddrc_hwmod_data->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ddrc_hwmod_data->regs_base))
+ return PTR_ERR(ddrc_hwmod_data->regs_base);
+
+ init_hisi_ddr(ddrc_hwmod_data->regs_base);
+
+ return 0;
+}
+
+static int init_hisi_ddrc_data(struct platform_device *pdev,
+ struct hisi_pmu *ddrc_pmu)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ddrc_pmu->hw_events.events = devm_kcalloc(dev,
+ ddrc_pmu->num_counters,
+ sizeof(*ddrc_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!ddrc_pmu->hw_events.events) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ raw_spin_lock_init(&ddrc_pmu->hw_events.pmu_lock);
+
+ init_hisi_ddrc_dts_data(pdev, ddrc_pmu->hwmod_data);
+
+ return 0;
+
+fail:
+ hisi_free_ddrc_data(ddrc_pmu);
+ return ret;
+}
+
+static struct attribute *hisi_ddrc_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_ddrc_format_group = {
+ .name = "format",
+ .attrs = hisi_ddrc_format_attr,
+};
+
+static struct attribute *hisi_ddrc_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(flux_write, "event=0x00"),
+ HISI_PMU_EVENT_ATTR_STR(flux_read, "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(flux_write_cmd, "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(flux_read_cmd, "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_write, "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_read, "event=0x05"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_write_cmd, "event=0x06"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_read_cmd, "event=0x07"),
+ HISI_PMU_EVENT_ATTR_STR(write_latency_cnt0,
+ "event=0x08"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt0,
+ "event=0x09"),
+ HISI_PMU_EVENT_ATTR_STR(write_latency_cnt1,
+ "event=0x0A"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt1,
+ "event=0x0B"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt_inher,
+ "event=0x0C"),
+ NULL,
+};
+
+static struct attribute_group hisi_ddrc_events_group = {
+ .name = "events",
+ .attrs = hisi_ddrc_events_attr,
+};
+
+static struct attribute *hisi_ddrc_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_ddrc_attr_group = {
+ .attrs = hisi_ddrc_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_ddrc_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_ddrc_cpumask_attr_group = {
+ .attrs = hisi_ddrc_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = {
+ &hisi_ddrc_attr_group,
+ &hisi_ddrc_format_group,
+ &hisi_ddrc_events_group,
+ &hisi_ddrc_cpumask_attr_group,
+ NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_ddrc_ops = {
+ .set_event_period = hisi_ddrc_set_event_period,
+ .get_event_idx = hisi_ddrc_get_event_idx,
+ .clear_event_idx = hisi_clear_ddrc_event_idx,
+ .event_update = hisi_ddrc_event_update,
+ .write_counter = hisi_write_ddrc_counter,
+};
+
+static int hisi_ddrc_pmu_init(struct device *dev,
+ struct hisi_pmu *ddrc_pmu)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data;
+ struct hisi_ddrc_hwcfg *ddrc_hwcfg;
+ int ret;
+
+ /* Read common PMU properties */
+ ret = hisi_uncore_common_fwprop_read(dev, ddrc_pmu);
+ if (ret)
+ return ret;
+
+ ddrc_hwmod_data = kzalloc(sizeof(struct hisi_ddrc_data),
+ GFP_KERNEL);
+ if (!ddrc_hwmod_data)
+ return -ENOMEM;
+
+ ddrc_hwcfg = &ddrc_hwmod_data->ddrc_hwcfg;
+ if (of_property_read_u32(dev->of_node, "ch-id",
+ &ddrc_hwcfg->channel_id)) {
+ kfree(ddrc_hwmod_data);
+ return -EINVAL;
+ }
+
+ ddrc_pmu->name = kasprintf(GFP_KERNEL, "hisi_ddrc%d_%d",
+ ddrc_pmu->scl_id, ddrc_hwcfg->channel_id);
+
+ ddrc_pmu->ops = &hisi_uncore_ddrc_ops;
+ ddrc_pmu->hwmod_data = ddrc_hwmod_data;
+ ddrc_pmu->dev = dev;
+
+ /* Pick one core to use for cpumask attributes */
+ cpumask_set_cpu(smp_processor_id(), &ddrc_pmu->cpu);
+
+ return 0;
+}
+
+static const struct of_device_id ddrc_of_match[] = {
+ { .compatible = "hisilicon,hisi-pmu-ddrc-v1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ddrc_of_match);
+
+static int hisi_pmu_ddrc_dev_probe(struct platform_device *pdev)
+{
+ struct hisi_pmu *ddrc_pmu;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ int ret;
+
+ of_id = of_match_device(ddrc_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ ddrc_pmu = hisi_pmu_alloc(dev);
+ if (IS_ERR(ddrc_pmu))
+ return PTR_ERR(ddrc_pmu);
+
+ ret = hisi_ddrc_pmu_init(dev, ddrc_pmu);
+ if (ret)
+ return ret;
+
+ ret = init_hisi_ddrc_data(pdev, ddrc_pmu);
+ if (ret)
+ goto fail_init;
+
+ /* Register with perf PMU */
+ ddrc_pmu->pmu = (struct pmu) {
+ .name = ddrc_pmu->name,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = hisi_uncore_pmu_event_init,
+ .add = hisi_uncore_pmu_add,
+ .del = hisi_uncore_pmu_del,
+ .start = hisi_uncore_pmu_start,
+ .stop = hisi_uncore_pmu_stop,
+ .read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_ddrc_pmu_attr_groups,
+ };
+
+ ret = hisi_uncore_pmu_setup(ddrc_pmu, ddrc_pmu->name);
+ if (ret) {
+ dev_err(ddrc_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, ddrc_pmu);
+
+ return 0;
+fail:
+ hisi_free_ddrc_data(ddrc_pmu);
+
+fail_init:
+ dev_err(ddrc_pmu->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static int hisi_pmu_ddrc_dev_remove(struct platform_device *pdev)
+{
+ struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev);
+
+ perf_pmu_unregister(&ddrc_pmu->pmu);
+ hisi_free_ddrc_data(ddrc_pmu);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver hisi_pmu_ddrc_driver = {
+ .driver = {
+ .name = "hisi-pmu-ddrc",
+ .of_match_table = ddrc_of_match,
+ },
+ .probe = hisi_pmu_ddrc_dev_probe,
+ .remove = hisi_pmu_ddrc_dev_remove,
+};
+module_platform_driver(hisi_pmu_ddrc_driver);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x DDRC PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.h b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
new file mode 100644
index 0000000..89eab6b
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
@@ -0,0 +1,73 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_DDRC_H__
+#define __HISI_UNCORE_DDRC_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon DDRC event types.
+ */
+enum armv8_hisi_ddrc_event_types {
+ HISI_HWEVENT_DDRC_FLUX_WR = 0x0,
+ HISI_HWEVENT_DDRC_FLUX_RD = 0x01,
+ HISI_HWEVENT_DDRC_FLUX_WCMD = 0x02,
+ HISI_HWEVENT_DDRC_FLUX_RCMD = 0x03,
+ HISI_HWEVENT_DDRC_FLUXID_WD = 0x04,
+ HISI_HWEVENT_DDRC_FLUXID_RD = 0x05,
+ HISI_HWEVENT_DDRC_FLUXID_WCMD = 0x06,
+ HISI_HWEVENT_DDRC_FLUXID_RCMD = 0x07,
+ HISI_HWEVENT_DDRC_WLAT_CNT0 = 0x08,
+ HISI_HWEVENT_DDRC_RLAT_CNT0 = 0x09,
+ HISI_HWEVENT_DDRC_WLAT_CNT1 = 0x0A,
+ HISI_HWEVENT_DDRC_RLAT_CNT1 = 0x0B,
+ HISI_HWEVENT_DDRC_INHERE_RLAT_CNT = 0x0C,
+ HISI_HWEVENT_DDRC_MAX_EVENT,
+};
+
+#define HISI_DDRC_CTRL_PERF_REG_OFF 0x010
+#define HISI_DDRC_CFG_PERF_REG_OFF 0x270
+#define HISI_DDRC_FLUX_WR_REG_OFF 0x380
+#define HISI_DDRC_FLUX_RD_REG_OFF 0x384
+#define HISI_DDRC_FLUX_WCMD_REG_OFF 0x388
+#define HISI_DDRC_FLUX_RCMD_REG_OFF 0x38C
+#define HISI_DDRC_FLUXID_WR_REG_OFF 0x390
+#define HISI_DDRC_FLUXID_RD_REG_OFF 0x394
+#define HISI_DDRC_FLUXID_WCMD_REG_OFF 0x398
+#define HISI_DDRC_FLUXID_RCMD_REG_OFF 0x39C
+#define HISI_DDRC_FLUX_WLATCNT0_REG_OFF 0x3A0
+#define HISI_DDRC_FLUX_RLAT_CNT0_REG_OFF 0x3A4
+#define HISI_DDRC_FLUX_WLATCNT1_REG_OFF 0x3A8
+#define HISI_DDRC_FLUX_RLAT_CNT1_REG_OFF 0x3AC
+
+struct hisi_ddrc_hwcfg {
+ u32 channel_id;
+};
+
+struct hisi_ddrc_data {
+ void __iomem *regs_base;
+ DECLARE_BITMAP(hisi_ddrc_event_used_mask,
+ HISI_HWEVENT_DDRC_MAX_EVENT);
+ struct hisi_ddrc_hwcfg ddrc_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_DDRC_H__ */
--
2.1.4
^ permalink raw reply related
* [RESEND PATCH v1 09/11] perf: hisi: Miscellanous node(MN) event counting in perf
From: Anurup M @ 2016-11-03 5:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478151727-20250-1-git-send-email-anurup.m@huawei.com>
From: Shaokun Zhang <zhangshaokun@hisilicon.com>
1. Add support to count MN hardware events.
2. Mn events are listed in sysfs at /sys/devices/hisi_mn2/events/
The events can be selected as shown in perf list
e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
event format is
-e "hisi_mn2/read_req/"
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
drivers/perf/hisilicon/Makefile | 2 +-
drivers/perf/hisilicon/hisi_uncore_mn.c | 571 ++++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_mn.h | 68 ++++
3 files changed, 640 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index e1766cf..8975104 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 0000000..920e346
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_mn.h"
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+ return (idx >= HISI_IDX_MN_COUNTER0 &&
+ idx <= HISI_IDX_MN_COUNTER_MAX);
+}
+
+static u32 hisi_read_mn_counter(struct hisi_mn_data *mn_hwmod_data,
+ int idx)
+{
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ struct hisi_djtag_client *client = mn_hwmod_data->client;
+ u32 cfg_en, reg_offset, value;
+
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+ reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off + (idx * 4);
+
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ struct hisi_pmu *pmn_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_mn_data *mn_hwmod_data;
+ u64 delta, prev_raw_count, new_raw_count = 0;
+ u32 cfg_en;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return 0;
+ }
+
+ mn_hwmod_data = pmn_pmu->hwmod_data;
+
+ /* Check if the MN data is initialized for this SCCL */
+ if (!mn_hwmod_data->client) {
+ dev_err(pmn_pmu->dev,
+ "SCL=%d not initialized!\n", pmn_pmu->scl_id);
+ return 0;
+ }
+
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count =
+ hisi_read_mn_counter(mn_hwmod_data, idx);
+ delta = (new_raw_count - prev_raw_count) &
+ HISI_MAX_PERIOD;
+
+ local64_add(delta, &event->count);
+ } while (local64_cmpxchg(
+ &hwc->prev_count, prev_raw_count, new_raw_count) !=
+ prev_raw_count);
+
+ return new_raw_count;
+}
+
+static void hisi_set_mn_evtype(struct hisi_pmu *pmn_pmu, int idx, u32 val)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 cfg_en, event_value, value = 0;
+
+ event_value = (val -
+ HISI_HWEVENT_MN_EO_BARR_REQ);
+
+ /* Value to write to event type register */
+ val = event_value << (8 * idx);
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Set the event in MN_EVENT_TYPE Register
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= val;
+
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static u32 hisi_write_mn_counter(struct hisi_pmu *pmn_pmu,
+ struct hw_perf_event *hwc, u32 value)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 cfg_en, reg_offset;
+ int ret;
+ int idx = GET_CNTR_IDX(hwc);
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return -EINVAL;
+ }
+
+ reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off +
+ (idx * 4);
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ ret = hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+
+ return ret;
+}
+
+static void hisi_enable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Set the event_bus_en bit in MN_EVENT_CTRL to enable counting
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client,
+ &value);
+
+ value |= event_en;
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static void hisi_disable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Clear the event_bus_en bit in MN event control
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(event_en);
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static void hisi_clear_mn_event_idx(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ void *bitmap_addr;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ bitmap_addr = mn_hwmod_data->hisi_mn_event_used_mask;
+
+ __clear_bit(idx, bitmap_addr);
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Clear the event in MN_EVENT_TYPE Register
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= (0xff << (8 * idx));
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static int hisi_mn_get_event_idx(struct hisi_pmu *pmn_pmu)
+{
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ int event_idx;
+
+ event_idx =
+ find_first_zero_bit(
+ mn_hwmod_data->hisi_mn_event_used_mask,
+ HISI_MAX_CFG_MN_CNTR);
+
+ if (event_idx == HISI_MAX_CFG_MN_CNTR)
+ return -EAGAIN;
+
+ __set_bit(event_idx,
+ mn_hwmod_data->hisi_mn_event_used_mask);
+
+ return event_idx;
+}
+
+static void hisi_free_mn_data(struct hisi_pmu *pmn_pmu)
+{
+ kfree(pmn_pmu->hwmod_data);
+ pmn_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_mn_hwcfg(struct device *dev,
+ struct hisi_mn_data *pmn_data)
+{
+ struct hisi_mn_hwcfg *pmn_hwcfg = &pmn_data->mn_hwcfg;
+ struct device_node *node = dev->of_node;
+
+ if (of_property_read_u32(node, "counter-reg",
+ &pmn_hwcfg->counter_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read counter-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evctrl-reg",
+ &pmn_hwcfg->event_ctrl_reg_off)) {
+ dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "event-en",
+ &pmn_hwcfg->event_enable)) {
+ dev_err(dev, "DT:Couldnot read event-en property!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evtype-reg",
+ &pmn_hwcfg->evtype_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "module-id",
+ &pmn_hwcfg->module_id)) {
+ dev_err(dev, "DT:Couldnot read module-id property!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "cfgen-map",
+ &pmn_hwcfg->bank_cfgen)) {
+ dev_err(dev, "DT:Couldnot read cfgen-map property!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int init_hisi_mn_data(struct device *dev,
+ struct hisi_pmu *pmn_pmu,
+ struct hisi_djtag_client *client)
+{
+ struct hisi_mn_data *mn_hwmod_data;
+ int ret;
+
+ mn_hwmod_data = kzalloc(sizeof(struct hisi_mn_data),
+ GFP_KERNEL);
+ if (!mn_hwmod_data)
+ return -ENOMEM;
+
+ /* Set the djtag Identifier */
+ mn_hwmod_data->client = client;
+
+ pmn_pmu->hw_events.events = devm_kcalloc(dev,
+ pmn_pmu->num_counters,
+ sizeof(*pmn_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!pmn_pmu->hw_events.events) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ raw_spin_lock_init(&pmn_pmu->hw_events.pmu_lock);
+
+ pmn_pmu->hwmod_data = mn_hwmod_data;
+
+ ret = init_hisi_mn_hwcfg(dev, mn_hwmod_data);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ hisi_free_mn_data(pmn_pmu);
+ return ret;
+}
+
+static struct attribute *hisi_mn_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_mn_format_group = {
+ .name = "format",
+ .attrs = hisi_mn_format_attr,
+};
+
+static struct attribute *hisi_mn_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(eo_barrier_req,
+ "event=0x0"),
+ HISI_PMU_EVENT_ATTR_STR(ec_barrier_req,
+ "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(dvm_op_req,
+ "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(dvm_sync_req,
+ "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(read_req,
+ "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(write_req,
+ "event=0x05"),
+ NULL,
+};
+
+static struct attribute_group hisi_mn_events_group = {
+ .name = "events",
+ .attrs = hisi_mn_events_attr,
+};
+
+static struct attribute *hisi_mn_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_mn_attr_group = {
+ .attrs = hisi_mn_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_mn_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_mn_cpumask_attr_group = {
+ .attrs = hisi_mn_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_mn_pmu_attr_groups[] = {
+ &hisi_mn_attr_group,
+ &hisi_mn_format_group,
+ &hisi_mn_events_group,
+ &hisi_mn_cpumask_attr_group,
+ NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_mn_ops = {
+ .set_evtype = hisi_set_mn_evtype,
+ .set_event_period = hisi_pmu_set_event_period,
+ .get_event_idx = hisi_mn_get_event_idx,
+ .clear_event_idx = hisi_clear_mn_event_idx,
+ .event_update = hisi_mn_event_update,
+ .enable_counter = hisi_enable_mn_counter,
+ .disable_counter = hisi_disable_mn_counter,
+ .write_counter = hisi_write_mn_counter,
+};
+
+static int hisi_mn_pmu_init(struct device *dev,
+ struct hisi_pmu *pmn_pmu)
+{
+ int ret;
+ /* Read common PMU properties */
+ ret = hisi_uncore_common_fwprop_read(dev, pmn_pmu);
+ if (ret)
+ return ret;
+
+ pmn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn%d",
+ pmn_pmu->scl_id);
+ pmn_pmu->ops = &hisi_uncore_mn_ops;
+
+ pmn_pmu->dev = dev;
+ /* Pick one core to use for cpumask attributes */
+ cpumask_set_cpu(smp_processor_id(), &pmn_pmu->cpu);
+
+ return 0;
+}
+
+static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pmn_pmu = NULL;
+ struct device *dev = &client->dev;
+ int ret;
+
+ pmn_pmu = hisi_pmu_alloc(dev);
+ if (IS_ERR(pmn_pmu))
+ return PTR_ERR(pmn_pmu);
+
+ ret = hisi_mn_pmu_init(dev, pmn_pmu);
+ if (ret)
+ return ret;
+
+ ret = init_hisi_mn_data(dev, pmn_pmu, client);
+ if (ret)
+ goto fail_init;
+
+ /* Register with perf PMU */
+ pmn_pmu->pmu = (struct pmu) {
+ .name = pmn_pmu->name,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = hisi_uncore_pmu_event_init,
+ .add = hisi_uncore_pmu_add,
+ .del = hisi_uncore_pmu_del,
+ .start = hisi_uncore_pmu_start,
+ .stop = hisi_uncore_pmu_stop,
+ .read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_mn_pmu_attr_groups,
+ };
+
+ ret = hisi_uncore_pmu_setup(pmn_pmu, pmn_pmu->name);
+ if (ret) {
+ dev_err(pmn_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+ goto fail;
+ }
+
+ /* Set the drv data to mn_pmu */
+ dev_set_drvdata(dev, pmn_pmu);
+
+ return 0;
+
+fail:
+ hisi_free_mn_data(pmn_pmu);
+
+fail_init:
+ if (pmn_pmu)
+ devm_kfree(dev, pmn_pmu);
+ dev_err(pmn_pmu->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static int hisi_pmu_mn_dev_remove(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pmn_pmu = NULL;
+ struct device *dev = &client->dev;
+
+ pmn_pmu = dev_get_drvdata(dev);
+
+ perf_pmu_unregister(&pmn_pmu->pmu);
+ hisi_free_mn_data(pmn_pmu);
+
+ return 0;
+}
+
+static const struct of_device_id mn_of_match[] = {
+ { .compatible = "hisilicon,hisi-pmu-mn-v1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mn_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_mn_driver = {
+ .driver = {
+ .name = "hisi-pmu-mn",
+ .of_match_table = mn_of_match,
+ },
+ .probe = hisi_pmu_mn_dev_probe,
+ .remove = hisi_pmu_mn_dev_remove,
+};
+
+static int __init hisi_pmu_mn_init(void)
+{
+ int rc;
+
+ rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_mn_driver);
+ if (rc < 0) {
+ pr_err("hisi pmu mn init failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+module_init(hisi_pmu_mn_init);
+
+static void __exit hisi_pmu_mn_exit(void)
+{
+ hisi_djtag_unregister_driver(&hisi_pmu_mn_driver);
+}
+module_exit(hisi_pmu_mn_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x MN PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shaokun Zhang");
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.h b/drivers/perf/hisilicon/hisi_uncore_mn.h
new file mode 100644
index 0000000..4cac2f3
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.h
@@ -0,0 +1,68 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_MN_H__
+#define __HISI_UNCORE_MN_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN RAW event types.
+ */
+enum armv8_hisi_mn_event_types {
+ HISI_HWEVENT_MN_EO_BARR_REQ = 0x0,
+ HISI_HWEVENT_MN_EC_BARR_REQ = 0x01,
+ HISI_HWEVENT_MN_DVM_OP_REQ = 0x02,
+ HISI_HWEVENT_MN_DVM_SYNC_REQ = 0x03,
+ HISI_HWEVENT_MN_READ_REQ = 0x04,
+ HISI_HWEVENT_MN_WRITE_REQ = 0x05,
+ HISI_HWEVENT_MN_COPYBK_REQ = 0x06,
+ HISI_HWEVENT_MN_OTHER_REQ = 0x07,
+ HISI_HWEVENT_MN_EVENT_MAX = 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+ HISI_IDX_MN_COUNTER0 = 0x0,
+ HISI_IDX_MN_COUNTER_MAX = 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR 0x04
+
+struct hisi_mn_hwcfg {
+ u32 evtype_reg0_off;
+ u32 counter_reg0_off;
+ u32 event_ctrl_reg_off;
+ u32 event_enable;
+ u32 module_id;
+ u32 bank_cfgen;
+};
+
+struct hisi_mn_data {
+ struct hisi_djtag_client *client;
+ DECLARE_BITMAP(hisi_mn_event_used_mask,
+ HISI_MAX_CFG_MN_CNTR);
+ struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_MN_H__ */
--
2.1.4
^ permalink raw reply related
* [RESEND PATCH v1 08/11] perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
From: Anurup M @ 2016-11-03 5:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478151727-20250-1-git-send-email-anurup.m@huawei.com>
1. Add L3 caches events to /sys/devices/hisi_l3c2/events/
The events can be selected as shown in perf list
e.g.: For L3C_READ_ALLOCATE event for Super CPU cluster 2 the
event format is
-e "hisi_l3c2/read_allocate/"
2. Add cpu_mask attribute group for showing the available CPU
for counting.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
drivers/perf/hisilicon/hisi_uncore_l3c.c | 57 ++++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.c | 40 ++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.h | 22 ++++++++++++
3 files changed, 119 insertions(+)
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
index f78f7b2..428fba0 100644
--- a/drivers/perf/hisilicon/hisi_uncore_l3c.c
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -436,6 +436,62 @@ static int init_hisi_l3c_data(struct device *dev,
return ret;
}
+static struct attribute *hisi_l3c_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_l3c_format_group = {
+ .name = "format",
+ .attrs = hisi_l3c_format_attr,
+};
+
+static struct attribute *hisi_l3c_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(read_allocate,
+ "event=0x0"),
+ HISI_PMU_EVENT_ATTR_STR(write_allocate,
+ "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(read_noallocate,
+ "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(write_noallocate,
+ "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(read_hit, "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(write_hit, "event=0x05"),
+ NULL,
+};
+
+static struct attribute_group hisi_l3c_events_group = {
+ .name = "events",
+ .attrs = hisi_l3c_events_attr,
+};
+
+static struct attribute *hisi_l3c_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_l3c_attr_group = {
+ .attrs = hisi_l3c_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_l3c_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_l3c_cpumask_attr_group = {
+ .attrs = hisi_l3c_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
+ &hisi_l3c_attr_group,
+ &hisi_l3c_format_group,
+ &hisi_l3c_events_group,
+ &hisi_l3c_cpumask_attr_group,
+ NULL,
+};
+
static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
.set_evtype = hisi_set_l3c_evtype,
.set_event_period = hisi_pmu_set_event_period,
@@ -496,6 +552,7 @@ static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
.start = hisi_uncore_pmu_start,
.stop = hisi_uncore_pmu_stop,
.read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_l3c_pmu_attr_groups,
};
ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
index 8d29fcc..d0a911a 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.c
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -26,6 +26,46 @@
#include <linux/perf_event.h>
#include "hisi_uncore_pmu.h"
+/*
+ * PMU format attributes
+ */
+ssize_t hisi_format_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr;
+
+ eattr = container_of(attr, struct dev_ext_attribute,
+ attr);
+ return sprintf(buf, "%s\n", (char *) eattr->var);
+}
+
+/*
+ * PMU event attributes
+ */
+ssize_t hisi_event_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct perf_pmu_events_attr *pmu_attr =
+ container_of(attr, struct perf_pmu_events_attr, attr);
+
+ if (pmu_attr->event_str)
+ return sprintf(buf, "%s", pmu_attr->event_str);
+
+ return 0;
+}
+
+/*
+ * sysfs cpumask attributes
+ */
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct hisi_pmu *hisi_pmu = to_hisi_pmu(pmu);
+
+ return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->cpu);
+}
+
/* djtag read interface - Call djtag driver to access SoC registers */
int hisi_djtag_readreg(int module_id, int bank, u32 offset,
struct hisi_djtag_client *client, u32 *pvalue)
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
index b6b16df..a948752 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.h
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -51,6 +51,22 @@
(((event_code & HISI_SCCL_MASK) >> \
HISI_SCCL_SHIFT) - 1)
+#define HISI_PMU_FORMAT_ATTR(_name, _config) \
+ (&((struct dev_ext_attribute[]) { \
+ { .attr = __ATTR(_name, 0444, \
+ hisi_format_sysfs_show, NULL), \
+ .var = (void *) _config, \
+ } \
+ })[0].attr.attr)
+
+#define HISI_PMU_EVENT_ATTR_STR(_name, _str) \
+ (&((struct perf_pmu_events_attr[]) { \
+ { .attr = __ATTR(_name, 0444, \
+ hisi_event_sysfs_show, NULL), \
+ .event_str = _str, \
+ } \
+ })[0].attr.attr)
+
struct hisi_pmu;
struct hisi_uncore_ops {
@@ -105,4 +121,10 @@ int hisi_djtag_writereg(int module_id, int bank,
struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
int hisi_uncore_common_fwprop_read(struct device *dev,
struct hisi_pmu *phisi_pmu);
+ssize_t hisi_event_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t hisi_format_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
#endif /* __HISI_UNCORE_PMU_H__ */
--
2.1.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox