Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
fcb59bdf
Commit
fcb59bdf
authored
Dec 10, 2015
by
Linus Walleij
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'bcm-nsp' into devel
parents
e43d2b75
8bfcbbbc
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
847 additions
and
0 deletions
+847
-0
Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt
Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt
+80
-0
drivers/pinctrl/bcm/Kconfig
drivers/pinctrl/bcm/Kconfig
+17
-0
drivers/pinctrl/bcm/Makefile
drivers/pinctrl/bcm/Makefile
+1
-0
drivers/pinctrl/bcm/pinctrl-nsp-gpio.c
drivers/pinctrl/bcm/pinctrl-nsp-gpio.c
+749
-0
No files found.
Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt
0 → 100644
View file @
fcb59bdf
Broadcom Northstar plus (NSP) GPIO/PINCONF Controller
Required properties:
- compatible:
Must be "brcm,nsp-gpio-a"
- reg:
Should contain the register physical address and length for each of
GPIO base, IO control registers
- #gpio-cells:
Must be two. The first cell is the GPIO pin number (within the
controller's pin space) and the second cell is used for the following:
bit[0]: polarity (0 for active high and 1 for active low)
- gpio-controller:
Specifies that the node is a GPIO controller
- ngpios:
Number of gpios supported (58x25 supports 32 and 58x23 supports 24)
Optional properties:
- interrupts:
Interrupt ID
- interrupt-controller:
Specifies that the node is an interrupt controller
- gpio-ranges:
Specifies the mapping between gpio controller and pin-controllers pins.
This requires 4 fields in cells defined as -
1. Phandle of pin-controller.
2. GPIO base pin offset.
3 Pin-control base pin offset.
4. number of gpio pins which are linearly mapped from pin base.
Supported generic PINCONF properties in child nodes:
- pins:
The list of pins (within the controller's own pin space) that properties
in the node apply to. Pin names are "gpio-<pin>"
- bias-disable:
Disable pin bias
- bias-pull-up:
Enable internal pull up resistor
- bias-pull-down:
Enable internal pull down resistor
- drive-strength:
Valid drive strength values include 2, 4, 6, 8, 10, 12, 14, 16 (mA)
Example:
gpioa: gpio@18000020 {
compatible = "brcm,nsp-gpio-a";
reg = <0x18000020 0x100>,
<0x1803f1c4 0x1c>;
#gpio-cells = <2>;
gpio-controller;
ngpios = <32>;
gpio-ranges = <&pinctrl 0 0 31>;
interrupt-controller;
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
/* Hog a few default settings */
pinctrl-names = "default";
pinctrl-0 = <&led>;
led: led {
pins = "gpio-1";
bias-pull-up;
};
pwr: pwr {
gpio-hog;
gpios = <3 1>;
output-high;
};
};
drivers/pinctrl/bcm/Kconfig
View file @
fcb59bdf
...
@@ -68,3 +68,20 @@ config PINCTRL_CYGNUS_MUX
...
@@ -68,3 +68,20 @@ config PINCTRL_CYGNUS_MUX
The Broadcom Cygnus IOMUX driver supports group based IOMUX
The Broadcom Cygnus IOMUX driver supports group based IOMUX
configuration, with the exception that certain individual pins
configuration, with the exception that certain individual pins
can be overrided to GPIO function
can be overrided to GPIO function
config PINCTRL_NSP_GPIO
bool "Broadcom NSP GPIO (with PINCONF) driver"
depends on OF_GPIO && (ARCH_BCM_NSP || COMPILE_TEST)
select GPIOLIB_IRQCHIP
select PINCONF
select GENERIC_PINCONF
default ARCH_BCM_NSP
help
Say yes here to enable the Broadcom NSP GPIO driver.
The Broadcom Northstar Plus SoC ChipcommonA GPIO controller is
supported by this driver.
The ChipcommonA GPIO controller support basic PINCONF functions such
as bias pull up, pull down, and drive strength configurations, when
these pins are muxed to GPIO.
drivers/pinctrl/bcm/Makefile
View file @
fcb59bdf
...
@@ -4,3 +4,4 @@ obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o
...
@@ -4,3 +4,4 @@ obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o
obj-$(CONFIG_PINCTRL_BCM2835)
+=
pinctrl-bcm2835.o
obj-$(CONFIG_PINCTRL_BCM2835)
+=
pinctrl-bcm2835.o
obj-$(CONFIG_PINCTRL_IPROC_GPIO)
+=
pinctrl-iproc-gpio.o
obj-$(CONFIG_PINCTRL_IPROC_GPIO)
+=
pinctrl-iproc-gpio.o
obj-$(CONFIG_PINCTRL_CYGNUS_MUX)
+=
pinctrl-cygnus-mux.o
obj-$(CONFIG_PINCTRL_CYGNUS_MUX)
+=
pinctrl-cygnus-mux.o
obj-$(CONFIG_PINCTRL_NSP_GPIO)
+=
pinctrl-nsp-gpio.o
drivers/pinctrl/bcm/pinctrl-nsp-gpio.c
0 → 100644
View file @
fcb59bdf
/*
* Copyright (C) 2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This file contains the Broadcom Northstar Plus (NSP) GPIO driver that
* supports the chipCommonA GPIO controller. Basic PINCONF such as bias,
* pull up/down, slew and drive strength are also supported in this driver.
*
* Pins from the chipCommonA GPIO can be individually muxed to GPIO function,
* through the interaction with the NSP IOMUX controller.
*/
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
#include "../pinctrl-utils.h"
#define NSP_CHIP_A_INT_STATUS 0x00
#define NSP_CHIP_A_INT_MASK 0x04
#define NSP_GPIO_DATA_IN 0x40
#define NSP_GPIO_DATA_OUT 0x44
#define NSP_GPIO_OUT_EN 0x48
#define NSP_GPIO_INT_POLARITY 0x50
#define NSP_GPIO_INT_MASK 0x54
#define NSP_GPIO_EVENT 0x58
#define NSP_GPIO_EVENT_INT_MASK 0x5c
#define NSP_GPIO_EVENT_INT_POLARITY 0x64
#define NSP_CHIP_A_GPIO_INT_BIT 0x01
/* I/O parameters offset for chipcommon A GPIO */
#define NSP_GPIO_DRV_CTRL 0x00
#define NSP_GPIO_HYSTERESIS_EN 0x10
#define NSP_GPIO_SLEW_RATE_EN 0x14
#define NSP_PULL_UP_EN 0x18
#define NSP_PULL_DOWN_EN 0x1c
#define GPIO_DRV_STRENGTH_BITS 0x03
/*
* nsp GPIO core
*
* @dev: pointer to device
* @base: I/O register base for nsp GPIO controller
* @io_ctrl: I/O register base for PINCONF support outside the GPIO block
* @gc: GPIO chip
* @pctl: pointer to pinctrl_dev
* @pctldesc: pinctrl descriptor
* @irq_domain: pointer to irq domain
* @lock: lock to protect access to I/O registers
*/
struct
nsp_gpio
{
struct
device
*
dev
;
void
__iomem
*
base
;
void
__iomem
*
io_ctrl
;
struct
gpio_chip
gc
;
struct
pinctrl_dev
*
pctl
;
struct
pinctrl_desc
pctldesc
;
struct
irq_domain
*
irq_domain
;
spinlock_t
lock
;
};
enum
base_type
{
REG
,
IO_CTRL
};
static
inline
struct
nsp_gpio
*
to_nsp_gpio
(
struct
gpio_chip
*
gc
)
{
return
container_of
(
gc
,
struct
nsp_gpio
,
gc
);
}
/*
* Mapping from PINCONF pins to GPIO pins is 1-to-1
*/
static
inline
unsigned
nsp_pin_to_gpio
(
unsigned
pin
)
{
return
pin
;
}
/*
* nsp_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
* nsp GPIO register
*
* @nsp_gpio: nsp GPIO device
* @base_type: reg base to modify
* @reg: register offset
* @gpio: GPIO pin
* @set: set or clear
*/
static
inline
void
nsp_set_bit
(
struct
nsp_gpio
*
chip
,
enum
base_type
address
,
unsigned
int
reg
,
unsigned
gpio
,
bool
set
)
{
u32
val
;
void
__iomem
*
base_address
;
if
(
address
==
IO_CTRL
)
base_address
=
chip
->
io_ctrl
;
else
base_address
=
chip
->
base
;
val
=
readl
(
base_address
+
reg
);
if
(
set
)
val
|=
BIT
(
gpio
);
else
val
&=
~
BIT
(
gpio
);
writel
(
val
,
base_address
+
reg
);
}
/*
* nsp_get_bit - get one bit (corresponding to the GPIO pin) in a
* nsp GPIO register
*/
static
inline
bool
nsp_get_bit
(
struct
nsp_gpio
*
chip
,
enum
base_type
address
,
unsigned
int
reg
,
unsigned
gpio
)
{
if
(
address
==
IO_CTRL
)
return
!!
(
readl
(
chip
->
io_ctrl
+
reg
)
&
BIT
(
gpio
));
else
return
!!
(
readl
(
chip
->
base
+
reg
)
&
BIT
(
gpio
));
}
static
irqreturn_t
nsp_gpio_irq_handler
(
int
irq
,
void
*
data
)
{
struct
nsp_gpio
*
chip
=
(
struct
nsp_gpio
*
)
data
;
struct
gpio_chip
gc
=
chip
->
gc
;
int
bit
;
unsigned
long
int_bits
=
0
;
u32
int_status
;
/* go through the entire GPIOs and handle all interrupts */
int_status
=
readl
(
chip
->
base
+
NSP_CHIP_A_INT_STATUS
);
if
(
int_status
&
NSP_CHIP_A_GPIO_INT_BIT
)
{
unsigned
int
event
,
level
;
/* Get level and edge interrupts */
event
=
readl
(
chip
->
base
+
NSP_GPIO_EVENT_INT_MASK
)
&
readl
(
chip
->
base
+
NSP_GPIO_EVENT
);
level
=
readl
(
chip
->
base
+
NSP_GPIO_DATA_IN
)
^
readl
(
chip
->
base
+
NSP_GPIO_INT_POLARITY
);
level
&=
readl
(
chip
->
base
+
NSP_GPIO_INT_MASK
);
int_bits
=
level
|
event
;
for_each_set_bit
(
bit
,
&
int_bits
,
gc
.
ngpio
)
{
/*
* Clear the interrupt before invoking the
* handler, so we do not leave any window
*/
writel
(
BIT
(
bit
),
chip
->
base
+
NSP_GPIO_EVENT
);
generic_handle_irq
(
irq_linear_revmap
(
chip
->
irq_domain
,
bit
));
}
}
return
int_bits
?
IRQ_HANDLED
:
IRQ_NONE
;
}
static
void
nsp_gpio_irq_ack
(
struct
irq_data
*
d
)
{
struct
nsp_gpio
*
chip
=
irq_data_get_irq_chip_data
(
d
);
unsigned
gpio
=
d
->
hwirq
;
u32
val
=
BIT
(
gpio
);
u32
trigger_type
;
trigger_type
=
irq_get_trigger_type
(
d
->
irq
);
if
(
trigger_type
&
(
IRQ_TYPE_EDGE_FALLING
|
IRQ_TYPE_EDGE_RISING
))
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_EVENT
,
gpio
,
val
);
}
/*
* nsp_gpio_irq_set_mask - mask/unmask a GPIO interrupt
*
* @d: IRQ chip data
* @unmask: mask/unmask GPIO interrupt
*/
static
void
nsp_gpio_irq_set_mask
(
struct
irq_data
*
d
,
bool
unmask
)
{
struct
nsp_gpio
*
chip
=
irq_data_get_irq_chip_data
(
d
);
unsigned
gpio
=
d
->
hwirq
;
u32
trigger_type
;
trigger_type
=
irq_get_trigger_type
(
d
->
irq
);
if
(
trigger_type
&
(
IRQ_TYPE_EDGE_FALLING
|
IRQ_TYPE_EDGE_RISING
))
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_EVENT_INT_MASK
,
gpio
,
unmask
);
else
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_INT_MASK
,
gpio
,
unmask
);
}
static
void
nsp_gpio_irq_mask
(
struct
irq_data
*
d
)
{
struct
nsp_gpio
*
chip
=
irq_data_get_irq_chip_data
(
d
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_gpio_irq_set_mask
(
d
,
false
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
}
static
void
nsp_gpio_irq_unmask
(
struct
irq_data
*
d
)
{
struct
nsp_gpio
*
chip
=
irq_data_get_irq_chip_data
(
d
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_gpio_irq_set_mask
(
d
,
true
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
}
static
int
nsp_gpio_irq_set_type
(
struct
irq_data
*
d
,
unsigned
int
type
)
{
struct
nsp_gpio
*
chip
=
irq_data_get_irq_chip_data
(
d
);
unsigned
gpio
=
d
->
hwirq
;
bool
level_low
;
bool
falling
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
falling
=
nsp_get_bit
(
chip
,
REG
,
NSP_GPIO_EVENT_INT_POLARITY
,
gpio
);
level_low
=
nsp_get_bit
(
chip
,
REG
,
NSP_GPIO_INT_POLARITY
,
gpio
);
switch
(
type
&
IRQ_TYPE_SENSE_MASK
)
{
case
IRQ_TYPE_EDGE_RISING
:
falling
=
false
;
break
;
case
IRQ_TYPE_EDGE_FALLING
:
falling
=
true
;
break
;
case
IRQ_TYPE_LEVEL_HIGH
:
level_low
=
false
;
break
;
case
IRQ_TYPE_LEVEL_LOW
:
level_low
=
true
;
break
;
default:
dev_err
(
chip
->
dev
,
"invalid GPIO IRQ type 0x%x
\n
"
,
type
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
-
EINVAL
;
}
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_EVENT_INT_POLARITY
,
gpio
,
falling
);
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_INT_POLARITY
,
gpio
,
level_low
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
dev_dbg
(
chip
->
dev
,
"gpio:%u level_low:%s falling:%s
\n
"
,
gpio
,
level_low
?
"true"
:
"false"
,
falling
?
"true"
:
"false"
);
return
0
;
}
static
struct
irq_chip
nsp_gpio_irq_chip
=
{
.
name
=
"gpio-a"
,
.
irq_enable
=
nsp_gpio_irq_unmask
,
.
irq_disable
=
nsp_gpio_irq_mask
,
.
irq_ack
=
nsp_gpio_irq_ack
,
.
irq_mask
=
nsp_gpio_irq_mask
,
.
irq_unmask
=
nsp_gpio_irq_unmask
,
.
irq_set_type
=
nsp_gpio_irq_set_type
,
};
/*
* Request the nsp IOMUX pinmux controller to mux individual pins to GPIO
*/
static
int
nsp_gpio_request
(
struct
gpio_chip
*
gc
,
unsigned
offset
)
{
unsigned
gpio
=
gc
->
base
+
offset
;
return
pinctrl_request_gpio
(
gpio
);
}
static
void
nsp_gpio_free
(
struct
gpio_chip
*
gc
,
unsigned
offset
)
{
unsigned
gpio
=
gc
->
base
+
offset
;
pinctrl_free_gpio
(
gpio
);
}
static
int
nsp_gpio_direction_input
(
struct
gpio_chip
*
gc
,
unsigned
gpio
)
{
struct
nsp_gpio
*
chip
=
to_nsp_gpio
(
gc
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_OUT_EN
,
gpio
,
false
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
dev_dbg
(
chip
->
dev
,
"gpio:%u set input
\n
"
,
gpio
);
return
0
;
}
static
int
nsp_gpio_direction_output
(
struct
gpio_chip
*
gc
,
unsigned
gpio
,
int
val
)
{
struct
nsp_gpio
*
chip
=
to_nsp_gpio
(
gc
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_OUT_EN
,
gpio
,
true
);
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_DATA_OUT
,
gpio
,
!!
(
val
));
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
dev_dbg
(
chip
->
dev
,
"gpio:%u set output, value:%d
\n
"
,
gpio
,
val
);
return
0
;
}
static
void
nsp_gpio_set
(
struct
gpio_chip
*
gc
,
unsigned
gpio
,
int
val
)
{
struct
nsp_gpio
*
chip
=
to_nsp_gpio
(
gc
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_set_bit
(
chip
,
REG
,
NSP_GPIO_DATA_OUT
,
gpio
,
!!
(
val
));
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
dev_dbg
(
chip
->
dev
,
"gpio:%u set, value:%d
\n
"
,
gpio
,
val
);
}
static
int
nsp_gpio_get
(
struct
gpio_chip
*
gc
,
unsigned
gpio
)
{
struct
nsp_gpio
*
chip
=
to_nsp_gpio
(
gc
);
return
!!
(
readl
(
chip
->
base
+
NSP_GPIO_DATA_IN
)
&
BIT
(
gpio
));
}
static
int
nsp_gpio_to_irq
(
struct
gpio_chip
*
gc
,
unsigned
offset
)
{
struct
nsp_gpio
*
chip
=
to_nsp_gpio
(
gc
);
return
irq_linear_revmap
(
chip
->
irq_domain
,
offset
);
}
static
int
nsp_get_groups_count
(
struct
pinctrl_dev
*
pctldev
)
{
return
1
;
}
/*
* Only one group: "gpio_grp", since this local pinctrl device only performs
* GPIO specific PINCONF configurations
*/
static
const
char
*
nsp_get_group_name
(
struct
pinctrl_dev
*
pctldev
,
unsigned
selector
)
{
return
"gpio_grp"
;
}
static
const
struct
pinctrl_ops
nsp_pctrl_ops
=
{
.
get_groups_count
=
nsp_get_groups_count
,
.
get_group_name
=
nsp_get_group_name
,
.
dt_node_to_map
=
pinconf_generic_dt_node_to_map_pin
,
.
dt_free_map
=
pinctrl_utils_dt_free_map
,
};
static
int
nsp_gpio_set_slew
(
struct
nsp_gpio
*
chip
,
unsigned
gpio
,
u16
slew
)
{
if
(
slew
)
nsp_set_bit
(
chip
,
IO_CTRL
,
NSP_GPIO_SLEW_RATE_EN
,
gpio
,
true
);
else
nsp_set_bit
(
chip
,
IO_CTRL
,
NSP_GPIO_SLEW_RATE_EN
,
gpio
,
false
);
return
0
;
}
static
int
nsp_gpio_set_pull
(
struct
nsp_gpio
*
chip
,
unsigned
gpio
,
bool
pull_up
,
bool
pull_down
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
nsp_set_bit
(
chip
,
IO_CTRL
,
NSP_PULL_DOWN_EN
,
gpio
,
pull_down
);
nsp_set_bit
(
chip
,
IO_CTRL
,
NSP_PULL_UP_EN
,
gpio
,
pull_up
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
dev_dbg
(
chip
->
dev
,
"gpio:%u set pullup:%d pulldown: %d
\n
"
,
gpio
,
pull_up
,
pull_down
);
return
0
;
}
static
void
nsp_gpio_get_pull
(
struct
nsp_gpio
*
chip
,
unsigned
gpio
,
bool
*
pull_up
,
bool
*
pull_down
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
*
pull_up
=
nsp_get_bit
(
chip
,
IO_CTRL
,
NSP_PULL_UP_EN
,
gpio
);
*
pull_down
=
nsp_get_bit
(
chip
,
IO_CTRL
,
NSP_PULL_DOWN_EN
,
gpio
);
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
}
static
int
nsp_gpio_set_strength
(
struct
nsp_gpio
*
chip
,
unsigned
gpio
,
u16
strength
)
{
u32
offset
,
shift
,
i
;
u32
val
;
unsigned
long
flags
;
/* make sure drive strength is supported */
if
(
strength
<
2
||
strength
>
16
||
(
strength
%
2
))
return
-
ENOTSUPP
;
shift
=
gpio
;
offset
=
NSP_GPIO_DRV_CTRL
;
dev_dbg
(
chip
->
dev
,
"gpio:%u set drive strength:%d mA
\n
"
,
gpio
,
strength
);
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
strength
=
(
strength
/
2
)
-
1
;
for
(
i
=
GPIO_DRV_STRENGTH_BITS
;
i
>
0
;
i
--
)
{
val
=
readl
(
chip
->
io_ctrl
+
offset
);
val
&=
~
BIT
(
shift
);
val
|=
((
strength
>>
(
i
-
1
))
&
0x1
)
<<
shift
;
writel
(
val
,
chip
->
io_ctrl
+
offset
);
offset
+=
4
;
}
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
0
;
}
static
int
nsp_gpio_get_strength
(
struct
nsp_gpio
*
chip
,
unsigned
gpio
,
u16
*
strength
)
{
unsigned
int
i
,
offset
,
shift
;
u32
val
;
unsigned
long
flags
;
offset
=
NSP_GPIO_DRV_CTRL
;
shift
=
gpio
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
*
strength
=
0
;
for
(
i
=
(
GPIO_DRV_STRENGTH_BITS
-
1
);
i
>=
0
;
i
--
)
{
val
=
readl
(
chip
->
io_ctrl
+
offset
)
&
BIT
(
shift
);
val
>>=
shift
;
*
strength
+=
(
val
<<
i
);
offset
+=
4
;
}
/* convert to mA */
*
strength
=
(
*
strength
+
1
)
*
2
;
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
0
;
}
int
nsp_pin_config_group_get
(
struct
pinctrl_dev
*
pctldev
,
unsigned
selector
,
unsigned
long
*
config
)
{
return
0
;
}
int
nsp_pin_config_group_set
(
struct
pinctrl_dev
*
pctldev
,
unsigned
selector
,
unsigned
long
*
configs
,
unsigned
num_configs
)
{
return
0
;
}
static
int
nsp_pin_config_get
(
struct
pinctrl_dev
*
pctldev
,
unsigned
pin
,
unsigned
long
*
config
)
{
struct
nsp_gpio
*
chip
=
pinctrl_dev_get_drvdata
(
pctldev
);
enum
pin_config_param
param
=
pinconf_to_config_param
(
*
config
);
unsigned
int
gpio
;
u16
arg
=
0
;
bool
pull_up
,
pull_down
;
int
ret
;
gpio
=
nsp_pin_to_gpio
(
pin
);
switch
(
param
)
{
case
PIN_CONFIG_BIAS_DISABLE
:
nsp_gpio_get_pull
(
chip
,
gpio
,
&
pull_up
,
&
pull_down
);
if
((
pull_up
==
false
)
&&
(
pull_down
==
false
))
return
0
;
else
return
-
EINVAL
;
case
PIN_CONFIG_BIAS_PULL_UP
:
nsp_gpio_get_pull
(
chip
,
gpio
,
&
pull_up
,
&
pull_down
);
if
(
pull_up
)
return
0
;
else
return
-
EINVAL
;
case
PIN_CONFIG_BIAS_PULL_DOWN
:
nsp_gpio_get_pull
(
chip
,
gpio
,
&
pull_up
,
&
pull_down
);
if
(
pull_down
)
return
0
;
else
return
-
EINVAL
;
case
PIN_CONFIG_DRIVE_STRENGTH
:
ret
=
nsp_gpio_get_strength
(
chip
,
gpio
,
&
arg
);
if
(
ret
)
return
ret
;
*
config
=
pinconf_to_config_packed
(
param
,
arg
);
return
0
;
default:
return
-
ENOTSUPP
;
}
}
static
int
nsp_pin_config_set
(
struct
pinctrl_dev
*
pctldev
,
unsigned
pin
,
unsigned
long
*
configs
,
unsigned
num_configs
)
{
struct
nsp_gpio
*
chip
=
pinctrl_dev_get_drvdata
(
pctldev
);
enum
pin_config_param
param
;
u16
arg
;
unsigned
int
i
,
gpio
;
int
ret
=
-
ENOTSUPP
;
gpio
=
nsp_pin_to_gpio
(
pin
);
for
(
i
=
0
;
i
<
num_configs
;
i
++
)
{
param
=
pinconf_to_config_param
(
configs
[
i
]);
arg
=
pinconf_to_config_argument
(
configs
[
i
]);
switch
(
param
)
{
case
PIN_CONFIG_BIAS_DISABLE
:
ret
=
nsp_gpio_set_pull
(
chip
,
gpio
,
false
,
false
);
if
(
ret
<
0
)
goto
out
;
break
;
case
PIN_CONFIG_BIAS_PULL_UP
:
ret
=
nsp_gpio_set_pull
(
chip
,
gpio
,
true
,
false
);
if
(
ret
<
0
)
goto
out
;
break
;
case
PIN_CONFIG_BIAS_PULL_DOWN
:
ret
=
nsp_gpio_set_pull
(
chip
,
gpio
,
false
,
true
);
if
(
ret
<
0
)
goto
out
;
break
;
case
PIN_CONFIG_DRIVE_STRENGTH
:
ret
=
nsp_gpio_set_strength
(
chip
,
gpio
,
arg
);
if
(
ret
<
0
)
goto
out
;
break
;
case
PIN_CONFIG_SLEW_RATE
:
ret
=
nsp_gpio_set_slew
(
chip
,
gpio
,
arg
);
if
(
ret
<
0
)
goto
out
;
break
;
default:
dev_err
(
chip
->
dev
,
"invalid configuration
\n
"
);
return
-
ENOTSUPP
;
}
}
out:
return
ret
;
}
static
const
struct
pinconf_ops
nsp_pconf_ops
=
{
.
is_generic
=
true
,
.
pin_config_get
=
nsp_pin_config_get
,
.
pin_config_set
=
nsp_pin_config_set
,
.
pin_config_group_get
=
nsp_pin_config_group_get
,
.
pin_config_group_set
=
nsp_pin_config_group_set
,
};
/*
* NSP GPIO controller supports some PINCONF related configurations such as
* pull up, pull down, slew and drive strength, when the pin is configured
* to GPIO.
*
* Here a local pinctrl device is created with simple 1-to-1 pin mapping to the
* local GPIO pins
*/
static
int
nsp_gpio_register_pinconf
(
struct
nsp_gpio
*
chip
)
{
struct
pinctrl_desc
*
pctldesc
=
&
chip
->
pctldesc
;
struct
pinctrl_pin_desc
*
pins
;
struct
gpio_chip
*
gc
=
&
chip
->
gc
;
int
i
;
pins
=
devm_kcalloc
(
chip
->
dev
,
gc
->
ngpio
,
sizeof
(
*
pins
),
GFP_KERNEL
);
if
(
!
pins
)
return
-
ENOMEM
;
for
(
i
=
0
;
i
<
gc
->
ngpio
;
i
++
)
{
pins
[
i
].
number
=
i
;
pins
[
i
].
name
=
devm_kasprintf
(
chip
->
dev
,
GFP_KERNEL
,
"gpio-%d"
,
i
);
if
(
!
pins
[
i
].
name
)
return
-
ENOMEM
;
}
pctldesc
->
name
=
dev_name
(
chip
->
dev
);
pctldesc
->
pctlops
=
&
nsp_pctrl_ops
;
pctldesc
->
pins
=
pins
;
pctldesc
->
npins
=
gc
->
ngpio
;
pctldesc
->
confops
=
&
nsp_pconf_ops
;
chip
->
pctl
=
pinctrl_register
(
pctldesc
,
chip
->
dev
,
chip
);
if
(
IS_ERR
(
chip
->
pctl
))
{
dev_err
(
chip
->
dev
,
"unable to register pinctrl device
\n
"
);
return
PTR_ERR
(
chip
->
pctl
);
}
return
0
;
}
static
const
struct
of_device_id
nsp_gpio_of_match
[]
=
{
{.
compatible
=
"brcm,nsp-gpio-a"
,},
{}
};
static
int
nsp_gpio_probe
(
struct
platform_device
*
pdev
)
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
resource
*
res
;
struct
nsp_gpio
*
chip
;
struct
gpio_chip
*
gc
;
u32
val
,
count
;
int
irq
,
ret
;
if
(
of_property_read_u32
(
pdev
->
dev
.
of_node
,
"ngpios"
,
&
val
))
{
dev_err
(
&
pdev
->
dev
,
"Missing ngpios OF property
\n
"
);
return
-
ENODEV
;
}
chip
=
devm_kzalloc
(
dev
,
sizeof
(
*
chip
),
GFP_KERNEL
);
if
(
!
chip
)
return
-
ENOMEM
;
chip
->
dev
=
dev
;
platform_set_drvdata
(
pdev
,
chip
);
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
chip
->
base
=
devm_ioremap_resource
(
dev
,
res
);
if
(
IS_ERR
(
chip
->
base
))
{
dev_err
(
dev
,
"unable to map I/O memory
\n
"
);
return
PTR_ERR
(
chip
->
base
);
}
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
1
);
chip
->
io_ctrl
=
devm_ioremap_resource
(
dev
,
res
);
if
(
IS_ERR
(
chip
->
io_ctrl
))
{
dev_err
(
dev
,
"unable to map I/O memory
\n
"
);
return
PTR_ERR
(
chip
->
io_ctrl
);
}
spin_lock_init
(
&
chip
->
lock
);
gc
=
&
chip
->
gc
;
gc
->
base
=
-
1
;
gc
->
can_sleep
=
false
;
gc
->
ngpio
=
val
;
gc
->
label
=
dev_name
(
dev
);
gc
->
dev
=
dev
;
gc
->
of_node
=
dev
->
of_node
;
gc
->
request
=
nsp_gpio_request
;
gc
->
free
=
nsp_gpio_free
;
gc
->
direction_input
=
nsp_gpio_direction_input
;
gc
->
direction_output
=
nsp_gpio_direction_output
;
gc
->
set
=
nsp_gpio_set
;
gc
->
get
=
nsp_gpio_get
;
gc
->
to_irq
=
nsp_gpio_to_irq
;
/* optional GPIO interrupt support */
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
>
0
)
{
/* Create irq domain so that each pin can be assigned an IRQ.*/
chip
->
irq_domain
=
irq_domain_add_linear
(
gc
->
of_node
,
gc
->
ngpio
,
&
irq_domain_simple_ops
,
chip
);
if
(
!
chip
->
irq_domain
)
{
dev_err
(
&
pdev
->
dev
,
"Couldn't allocate IRQ domain
\n
"
);
return
-
ENXIO
;
}
/* Map each gpio to an IRQ and set the handler for gpiolib. */
for
(
count
=
0
;
count
<
gc
->
ngpio
;
count
++
)
{
int
irq
=
irq_create_mapping
(
chip
->
irq_domain
,
count
);
irq_set_chip_and_handler
(
irq
,
&
nsp_gpio_irq_chip
,
handle_simple_irq
);
set_irq_flags
(
irq
,
IRQF_VALID
);
irq_set_chip_data
(
irq
,
chip
);
}
/* Install ISR for this GPIO controller. */
ret
=
devm_request_irq
(
&
pdev
->
dev
,
irq
,
nsp_gpio_irq_handler
,
IRQF_SHARED
,
"gpio-a"
,
chip
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Unable to request IRQ%d: %d
\n
"
,
irq
,
ret
);
goto
err_rm_gpiochip
;
}
val
=
readl
(
chip
->
base
+
NSP_CHIP_A_INT_MASK
);
val
=
val
|
NSP_CHIP_A_GPIO_INT_BIT
;
writel
(
val
,
(
chip
->
base
+
NSP_CHIP_A_INT_MASK
));
}
ret
=
gpiochip_add
(
gc
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"unable to add GPIO chip
\n
"
);
return
ret
;
}
ret
=
nsp_gpio_register_pinconf
(
chip
);
if
(
ret
)
{
dev_err
(
dev
,
"unable to register pinconf
\n
"
);
goto
err_rm_gpiochip
;
}
return
0
;
err_rm_gpiochip:
gpiochip_remove
(
gc
);
return
ret
;
}
static
struct
platform_driver
nsp_gpio_driver
=
{
.
driver
=
{
.
name
=
"nsp-gpio-a"
,
.
of_match_table
=
nsp_gpio_of_match
,
},
.
probe
=
nsp_gpio_probe
,
};
static
int
__init
nsp_gpio_init
(
void
)
{
return
platform_driver_probe
(
&
nsp_gpio_driver
,
nsp_gpio_probe
);
}
arch_initcall_sync
(
nsp_gpio_init
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment