aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/bcm/pinctrl-iproc-gpio.c')
-rw-r--r--drivers/pinctrl/bcm/pinctrl-iproc-gpio.c158
1 files changed, 117 insertions, 41 deletions
diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
index b70058caee50..6f7d3a2f2e97 100644
--- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
+++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
@@ -54,8 +54,12 @@
/* drive strength control for ASIU GPIO */
#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
-/* drive strength control for CCM/CRMU (AON) GPIO */
-#define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00
+/* pinconf for CCM GPIO */
+#define IPROC_GPIO_PULL_DN_OFFSET 0x10
+#define IPROC_GPIO_PULL_UP_OFFSET 0x14
+
+/* pinconf for CRMU(aon) GPIO and CCM GPIO*/
+#define IPROC_GPIO_DRV_CTRL_OFFSET 0x00
#define GPIO_BANK_SIZE 0x200
#define NGPIOS_PER_BANK 32
@@ -76,6 +80,12 @@ enum iproc_pinconf_param {
IPROC_PINCON_MAX,
};
+enum iproc_pinconf_ctrl_type {
+ IOCTRL_TYPE_AON = 1,
+ IOCTRL_TYPE_CDRU,
+ IOCTRL_TYPE_INVALID,
+};
+
/*
* Iproc GPIO core
*
@@ -100,9 +110,11 @@ struct iproc_gpio {
void __iomem *base;
void __iomem *io_ctrl;
+ enum iproc_pinconf_ctrl_type io_ctrl_type;
raw_spinlock_t lock;
+ struct irq_chip irqchip;
struct gpio_chip gc;
unsigned num_banks;
@@ -291,14 +303,6 @@ static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return 0;
}
-static struct irq_chip iproc_gpio_irq_chip = {
- .name = "bcm-iproc-gpio",
- .irq_ack = iproc_gpio_irq_ack,
- .irq_mask = iproc_gpio_irq_mask,
- .irq_unmask = iproc_gpio_irq_unmask,
- .irq_set_type = iproc_gpio_irq_set_type,
-};
-
/*
* Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO
*/
@@ -355,6 +359,15 @@ static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
return 0;
}
+static int iproc_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct iproc_gpio *chip = gpiochip_get_data(gc);
+ unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
+
+ return !(readl(chip->base + offset) & BIT(shift));
+}
+
static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val)
{
struct iproc_gpio *chip = gpiochip_get_data(gc);
@@ -461,20 +474,44 @@ static const struct pinctrl_ops iproc_pctrl_ops = {
static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio,
bool disable, bool pull_up)
{
+ void __iomem *base;
unsigned long flags;
+ unsigned int shift;
+ u32 val_1, val_2;
raw_spin_lock_irqsave(&chip->lock, flags);
-
- if (disable) {
- iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
+ if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) {
+ base = chip->io_ctrl;
+ shift = IPROC_GPIO_SHIFT(gpio);
+
+ val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET);
+ val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET);
+ if (disable) {
+ /* no pull-up or pull-down */
+ val_1 &= ~BIT(shift);
+ val_2 &= ~BIT(shift);
+ } else if (pull_up) {
+ val_1 |= BIT(shift);
+ val_2 &= ~BIT(shift);
+ } else {
+ val_1 &= ~BIT(shift);
+ val_2 |= BIT(shift);
+ }
+ writel(val_1, base + IPROC_GPIO_PULL_UP_OFFSET);
+ writel(val_2, base + IPROC_GPIO_PULL_DN_OFFSET);
} else {
- iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio,
- pull_up);
- iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
+ if (disable) {
+ iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio,
+ false);
+ } else {
+ iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio,
+ pull_up);
+ iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio,
+ true);
+ }
}
raw_spin_unlock_irqrestore(&chip->lock, flags);
-
dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up);
return 0;
@@ -483,14 +520,35 @@ static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio,
static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio,
bool *disable, bool *pull_up)
{
+ void __iomem *base;
unsigned long flags;
+ unsigned int shift;
+ u32 val_1, val_2;
raw_spin_lock_irqsave(&chip->lock, flags);
- *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio);
- *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio);
+ if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) {
+ base = chip->io_ctrl;
+ shift = IPROC_GPIO_SHIFT(gpio);
+
+ val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET) & BIT(shift);
+ val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET) & BIT(shift);
+
+ *pull_up = val_1 ? true : false;
+ *disable = (val_1 | val_2) ? false : true;
+
+ } else {
+ *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio);
+ *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio);
+ }
raw_spin_unlock_irqrestore(&chip->lock, flags);
}
+#define DRV_STRENGTH_OFFSET(gpio, bit, type) ((type) == IOCTRL_TYPE_AON ? \
+ ((2 - (bit)) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \
+ ((type) == IOCTRL_TYPE_CDRU) ? \
+ ((bit) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \
+ ((bit) * 4 + IPROC_GPIO_REG(gpio, IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET)))
+
static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio,
unsigned strength)
{
@@ -505,11 +563,8 @@ static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio,
if (chip->io_ctrl) {
base = chip->io_ctrl;
- offset = IPROC_GPIO_DRV0_CTRL_OFFSET;
} else {
base = chip->base;
- offset = IPROC_GPIO_REG(gpio,
- IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET);
}
shift = IPROC_GPIO_SHIFT(gpio);
@@ -520,11 +575,11 @@ static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio,
raw_spin_lock_irqsave(&chip->lock, flags);
strength = (strength / 2) - 1;
for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+ offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type);
val = readl(base + offset);
val &= ~BIT(shift);
val |= ((strength >> i) & 0x1) << shift;
writel(val, base + offset);
- offset += 4;
}
raw_spin_unlock_irqrestore(&chip->lock, flags);
@@ -541,11 +596,8 @@ static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio,
if (chip->io_ctrl) {
base = chip->io_ctrl;
- offset = IPROC_GPIO_DRV0_CTRL_OFFSET;
} else {
base = chip->base;
- offset = IPROC_GPIO_REG(gpio,
- IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET);
}
shift = IPROC_GPIO_SHIFT(gpio);
@@ -553,10 +605,10 @@ static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio,
raw_spin_lock_irqsave(&chip->lock, flags);
*strength = 0;
for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+ offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type);
val = readl(base + offset) & BIT(shift);
val >>= shift;
*strength += (val << i);
- offset += 4;
}
/* convert to mA */
@@ -734,6 +786,7 @@ static int iproc_gpio_probe(struct platform_device *pdev)
u32 ngpios, pinconf_disable_mask = 0;
int irq, ret;
bool no_pinconf = false;
+ enum iproc_pinconf_ctrl_type io_ctrl_type = IOCTRL_TYPE_INVALID;
/* NSP does not support drive strength config */
if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio"))
@@ -764,8 +817,15 @@ static int iproc_gpio_probe(struct platform_device *pdev)
dev_err(dev, "unable to map I/O memory\n");
return PTR_ERR(chip->io_ctrl);
}
+ if (of_device_is_compatible(dev->of_node,
+ "brcm,cygnus-ccm-gpio"))
+ io_ctrl_type = IOCTRL_TYPE_CDRU;
+ else
+ io_ctrl_type = IOCTRL_TYPE_AON;
}
+ chip->io_ctrl_type = io_ctrl_type;
+
if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
dev_err(&pdev->dev, "missing ngpios DT property\n");
return -ENODEV;
@@ -784,12 +844,42 @@ static int iproc_gpio_probe(struct platform_device *pdev)
gc->free = iproc_gpio_free;
gc->direction_input = iproc_gpio_direction_input;
gc->direction_output = iproc_gpio_direction_output;
+ gc->get_direction = iproc_gpio_get_direction;
gc->set = iproc_gpio_set;
gc->get = iproc_gpio_get;
chip->pinmux_is_supported = of_property_read_bool(dev->of_node,
"gpio-ranges");
+ /* optional GPIO interrupt support */
+ irq = platform_get_irq(pdev, 0);
+ if (irq) {
+ struct irq_chip *irqc;
+ struct gpio_irq_chip *girq;
+
+ irqc = &chip->irqchip;
+ irqc->name = "bcm-iproc-gpio";
+ irqc->irq_ack = iproc_gpio_irq_ack;
+ irqc->irq_mask = iproc_gpio_irq_mask;
+ irqc->irq_unmask = iproc_gpio_irq_unmask;
+ irqc->irq_set_type = iproc_gpio_irq_set_type;
+ irqc->irq_enable = iproc_gpio_irq_unmask;
+ irqc->irq_disable = iproc_gpio_irq_mask;
+
+ girq = &gc->irq;
+ girq->chip = irqc;
+ girq->parent_handler = iproc_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, 1,
+ sizeof(*girq->parents),
+ GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_simple_irq;
+ }
+
ret = gpiochip_add_data(gc, chip);
if (ret < 0) {
dev_err(dev, "unable to add GPIO chip\n");
@@ -814,20 +904,6 @@ static int iproc_gpio_probe(struct platform_device *pdev)
}
}
- /* optional GPIO interrupt support */
- irq = platform_get_irq(pdev, 0);
- if (irq) {
- ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "no GPIO irqchip\n");
- goto err_rm_gpiochip;
- }
-
- gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq,
- iproc_gpio_irq_handler);
- }
-
return 0;
err_rm_gpiochip: