diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/pinctrl/pinctrl-lpc18xx.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/pinctrl/pinctrl-lpc18xx.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-lpc18xx.c | 1382 |
1 files changed, 1382 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-lpc18xx.c b/drivers/pinctrl/pinctrl-lpc18xx.c new file mode 100644 index 000000000..13c041dd2 --- /dev/null +++ b/drivers/pinctrl/pinctrl-lpc18xx.c @@ -0,0 +1,1382 @@ +/* + * Pinctrl driver for NXP LPC18xx/LPC43xx System Control Unit (SCU) + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> + +#include "core.h" +#include "pinctrl-utils.h" + +/* LPC18XX SCU analog function registers */ +#define LPC18XX_SCU_REG_ENAIO0 0xc88 +#define LPC18XX_SCU_REG_ENAIO1 0xc8c +#define LPC18XX_SCU_REG_ENAIO2 0xc90 +#define LPC18XX_SCU_REG_ENAIO2_DAC BIT(0) + +/* LPC18XX SCU pin register definitions */ +#define LPC18XX_SCU_PIN_MODE_MASK 0x7 +#define LPC18XX_SCU_PIN_EPD BIT(3) +#define LPC18XX_SCU_PIN_EPUN BIT(4) +#define LPC18XX_SCU_PIN_EHS BIT(5) +#define LPC18XX_SCU_PIN_EZI BIT(6) +#define LPC18XX_SCU_PIN_ZIF BIT(7) +#define LPC18XX_SCU_PIN_EHD_MASK 0x300 +#define LPC18XX_SCU_PIN_EHD_POS 8 + +#define LPC18XX_SCU_USB1_EPD BIT(2) +#define LPC18XX_SCU_USB1_EPWR BIT(4) + +#define LPC18XX_SCU_I2C0_EFP BIT(0) +#define LPC18XX_SCU_I2C0_EHD BIT(2) +#define LPC18XX_SCU_I2C0_EZI BIT(3) +#define LPC18XX_SCU_I2C0_ZIF BIT(7) +#define LPC18XX_SCU_I2C0_SCL_SHIFT 0 +#define LPC18XX_SCU_I2C0_SDA_SHIFT 8 + +#define LPC18XX_SCU_FUNC_PER_PIN 8 + +/* LPC18XX SCU pin interrupt select registers */ +#define LPC18XX_SCU_PINTSEL0 0xe00 +#define LPC18XX_SCU_PINTSEL1 0xe04 +#define LPC18XX_SCU_PINTSEL_VAL_MASK 0xff +#define LPC18XX_SCU_PINTSEL_PORT_SHIFT 5 +#define LPC18XX_SCU_IRQ_PER_PINTSEL 4 +#define LPC18XX_GPIO_PINS_PER_PORT 32 +#define LPC18XX_GPIO_PIN_INT_MAX 8 + +#define LPC18XX_SCU_PINTSEL_VAL(val, n) \ + ((val) << (((n) % LPC18XX_SCU_IRQ_PER_PINTSEL) * 8)) + +/* LPC18xx pin types */ +enum { + TYPE_ND, /* Normal-drive */ + TYPE_HD, /* High-drive */ + TYPE_HS, /* High-speed */ + TYPE_I2C0, + TYPE_USB1, +}; + +/* LPC18xx pin functions */ +enum { + FUNC_R, /* Reserved */ + FUNC_ADC, + FUNC_ADCTRIG, + FUNC_CAN0, + FUNC_CAN1, + FUNC_CGU_OUT, + FUNC_CLKIN, + FUNC_CLKOUT, + FUNC_CTIN, + FUNC_CTOUT, + FUNC_DAC, + FUNC_EMC, + FUNC_EMC_ALT, + FUNC_ENET, + FUNC_ENET_ALT, + FUNC_GPIO, + FUNC_I2C0, + FUNC_I2C1, + FUNC_I2S0_RX_MCLK, + FUNC_I2S0_RX_SCK, + FUNC_I2S0_RX_SDA, + FUNC_I2S0_RX_WS, + FUNC_I2S0_TX_MCLK, + FUNC_I2S0_TX_SCK, + FUNC_I2S0_TX_SDA, + FUNC_I2S0_TX_WS, + FUNC_I2S1, + FUNC_LCD, + FUNC_LCD_ALT, + FUNC_MCTRL, + FUNC_NMI, + FUNC_QEI, + FUNC_SDMMC, + FUNC_SGPIO, + FUNC_SPI, + FUNC_SPIFI, + FUNC_SSP0, + FUNC_SSP0_ALT, + FUNC_SSP1, + FUNC_TIMER0, + FUNC_TIMER1, + FUNC_TIMER2, + FUNC_TIMER3, + FUNC_TRACE, + FUNC_UART0, + FUNC_UART1, + FUNC_UART2, + FUNC_UART3, + FUNC_USB0, + FUNC_USB1, + FUNC_MAX +}; + +static const char *const lpc18xx_function_names[] = { + [FUNC_R] = "reserved", + [FUNC_ADC] = "adc", + [FUNC_ADCTRIG] = "adctrig", + [FUNC_CAN0] = "can0", + [FUNC_CAN1] = "can1", + [FUNC_CGU_OUT] = "cgu_out", + [FUNC_CLKIN] = "clkin", + [FUNC_CLKOUT] = "clkout", + [FUNC_CTIN] = "ctin", + [FUNC_CTOUT] = "ctout", + [FUNC_DAC] = "dac", + [FUNC_EMC] = "emc", + [FUNC_EMC_ALT] = "emc_alt", + [FUNC_ENET] = "enet", + [FUNC_ENET_ALT] = "enet_alt", + [FUNC_GPIO] = "gpio", + [FUNC_I2C0] = "i2c0", + [FUNC_I2C1] = "i2c1", + [FUNC_I2S0_RX_MCLK] = "i2s0_rx_mclk", + [FUNC_I2S0_RX_SCK] = "i2s0_rx_sck", + [FUNC_I2S0_RX_SDA] = "i2s0_rx_sda", + [FUNC_I2S0_RX_WS] = "i2s0_rx_ws", + [FUNC_I2S0_TX_MCLK] = "i2s0_tx_mclk", + [FUNC_I2S0_TX_SCK] = "i2s0_tx_sck", + [FUNC_I2S0_TX_SDA] = "i2s0_tx_sda", + [FUNC_I2S0_TX_WS] = "i2s0_tx_ws", + [FUNC_I2S1] = "i2s1", + [FUNC_LCD] = "lcd", + [FUNC_LCD_ALT] = "lcd_alt", + [FUNC_MCTRL] = "mctrl", + [FUNC_NMI] = "nmi", + [FUNC_QEI] = "qei", + [FUNC_SDMMC] = "sdmmc", + [FUNC_SGPIO] = "sgpio", + [FUNC_SPI] = "spi", + [FUNC_SPIFI] = "spifi", + [FUNC_SSP0] = "ssp0", + [FUNC_SSP0_ALT] = "ssp0_alt", + [FUNC_SSP1] = "ssp1", + [FUNC_TIMER0] = "timer0", + [FUNC_TIMER1] = "timer1", + [FUNC_TIMER2] = "timer2", + [FUNC_TIMER3] = "timer3", + [FUNC_TRACE] = "trace", + [FUNC_UART0] = "uart0", + [FUNC_UART1] = "uart1", + [FUNC_UART2] = "uart2", + [FUNC_UART3] = "uart3", + [FUNC_USB0] = "usb0", + [FUNC_USB1] = "usb1", +}; + +struct lpc18xx_pmx_func { + const char **groups; + unsigned ngroups; +}; + +struct lpc18xx_scu_data { + struct pinctrl_dev *pctl; + void __iomem *base; + struct clk *clk; + struct lpc18xx_pmx_func func[FUNC_MAX]; +}; + +struct lpc18xx_pin_caps { + unsigned int offset; + unsigned char functions[LPC18XX_SCU_FUNC_PER_PIN]; + unsigned char analog; + unsigned char type; +}; + +/* Analog pins are required to have both bias and input disabled */ +#define LPC18XX_SCU_ANALOG_PIN_CFG 0x10 + +/* Macros to maniupluate analog member in lpc18xx_pin_caps */ +#define LPC18XX_ANALOG_PIN BIT(7) +#define LPC18XX_ANALOG_ADC(a) ((a >> 5) & 0x3) +#define LPC18XX_ANALOG_BIT_MASK 0x1f +#define ADC0 (LPC18XX_ANALOG_PIN | (0x00 << 5)) +#define ADC1 (LPC18XX_ANALOG_PIN | (0x01 << 5)) +#define DAC LPC18XX_ANALOG_PIN + +#define LPC_P(port, pin, f0, f1, f2, f3, f4, f5, f6, f7, a, t) \ +static struct lpc18xx_pin_caps lpc18xx_pin_p##port##_##pin = { \ + .offset = 0x##port * 32 * 4 + pin * 4, \ + .functions = { \ + FUNC_##f0, FUNC_##f1, FUNC_##f2, \ + FUNC_##f3, FUNC_##f4, FUNC_##f5, \ + FUNC_##f6, FUNC_##f7, \ + }, \ + .analog = a, \ + .type = TYPE_##t, \ +} + +#define LPC_N(pname, off, f0, f1, f2, f3, f4, f5, f6, f7, a, t) \ +static struct lpc18xx_pin_caps lpc18xx_pin_##pname = { \ + .offset = off, \ + .functions = { \ + FUNC_##f0, FUNC_##f1, FUNC_##f2, \ + FUNC_##f3, FUNC_##f4, FUNC_##f5, \ + FUNC_##f6, FUNC_##f7, \ + }, \ + .analog = a, \ + .type = TYPE_##t, \ +} + + +/* Pinmuxing table taken from data sheet */ +/* Pin FUNC0 FUNC1 FUNC2 FUNC3 FUNC4 FUNC5 FUNC6 FUNC7 ANALOG TYPE */ +LPC_P(0,0, GPIO, SSP1, ENET, SGPIO, R, R, I2S0_TX_WS,I2S1, 0, ND); +LPC_P(0,1, GPIO, SSP1,ENET_ALT,SGPIO, R, R, ENET, I2S1, 0, ND); +LPC_P(1,0, GPIO, CTIN, EMC, R, R, SSP0, SGPIO, R, 0, ND); +LPC_P(1,1, GPIO, CTOUT, EMC, SGPIO, R, SSP0, R, R, 0, ND); +LPC_P(1,2, GPIO, CTOUT, EMC, SGPIO, R, SSP0, R, R, 0, ND); +LPC_P(1,3, GPIO, CTOUT, SGPIO, EMC, USB0, SSP1, R, SDMMC, 0, ND); +LPC_P(1,4, GPIO, CTOUT, SGPIO, EMC, USB0, SSP1, R, SDMMC, 0, ND); +LPC_P(1,5, GPIO, CTOUT, R, EMC, USB0, SSP1, SGPIO, SDMMC, 0, ND); +LPC_P(1,6, GPIO, CTIN, R, EMC, R, R, SGPIO, SDMMC, 0, ND); +LPC_P(1,7, GPIO, UART1, CTOUT, EMC, USB0, R, R, R, 0, ND); +LPC_P(1,8, GPIO, UART1, CTOUT, EMC, R, R, R, SDMMC, 0, ND); +LPC_P(1,9, GPIO, UART1, CTOUT, EMC, R, R, R, SDMMC, 0, ND); +LPC_P(1,10, GPIO, UART1, CTOUT, EMC, R, R, R, SDMMC, 0, ND); +LPC_P(1,11, GPIO, UART1, CTOUT, EMC, R, R, R, SDMMC, 0, ND); +LPC_P(1,12, GPIO, UART1, R, EMC, TIMER0, R, SGPIO, SDMMC, 0, ND); +LPC_P(1,13, GPIO, UART1, R, EMC, TIMER0, R, SGPIO, SDMMC, 0, ND); +LPC_P(1,14, GPIO, UART1, R, EMC, TIMER0, R, SGPIO, R, 0, ND); +LPC_P(1,15, GPIO, UART2, SGPIO, ENET, TIMER0, R, R, R, 0, ND); +LPC_P(1,16, GPIO, UART2, SGPIO,ENET_ALT,TIMER0, R, R, ENET, 0, ND); +LPC_P(1,17, GPIO, UART2, R, ENET, TIMER0, CAN1, SGPIO, R, 0, HD); +LPC_P(1,18, GPIO, UART2, R, ENET, TIMER0, CAN1, SGPIO, R, 0, ND); +LPC_P(1,19, ENET, SSP1, R, R, CLKOUT, R, I2S0_RX_MCLK,I2S1, 0, ND); +LPC_P(1,20, GPIO, SSP1, R, ENET, TIMER0, R, SGPIO, R, 0, ND); +LPC_P(2,0, SGPIO, UART0, EMC, USB0, GPIO, R, TIMER3, ENET, 0, ND); +LPC_P(2,1, SGPIO, UART0, EMC, USB0, GPIO, R, TIMER3, R, 0, ND); +LPC_P(2,2, SGPIO, UART0, EMC, USB0, GPIO, CTIN, TIMER3, R, 0, ND); +LPC_P(2,3, SGPIO, I2C1, UART3, CTIN, GPIO, R, TIMER3, USB0, 0, HD); +LPC_P(2,4, SGPIO, I2C1, UART3, CTIN, GPIO, R, TIMER3, USB0, 0, HD); +LPC_P(2,5, SGPIO, CTIN, USB1, ADCTRIG, GPIO, R, TIMER3, USB0, 0, HD); +LPC_P(2,6, SGPIO, UART0, EMC, USB0, GPIO, CTIN, TIMER3, R, 0, ND); +LPC_P(2,7, GPIO, CTOUT, UART3, EMC, R, R, TIMER3, R, 0, ND); +LPC_P(2,8, SGPIO, CTOUT, UART3, EMC, GPIO, R, R, R, 0, ND); +LPC_P(2,9, GPIO, CTOUT, UART3, EMC, R, R, R, R, 0, ND); +LPC_P(2,10, GPIO, CTOUT, UART2, EMC, R, R, R, R, 0, ND); +LPC_P(2,11, GPIO, CTOUT, UART2, EMC, R, R, R, R, 0, ND); +LPC_P(2,12, GPIO, CTOUT, R, EMC, R, R, R, UART2, 0, ND); +LPC_P(2,13, GPIO, CTIN, R, EMC, R, R, R, UART2, 0, ND); +LPC_P(3,0, I2S0_RX_SCK, I2S0_RX_MCLK, I2S0_TX_SCK, I2S0_TX_MCLK,SSP0,R,R,R, 0, ND); +LPC_P(3,1, I2S0_TX_WS, I2S0_RX_WS,CAN0,USB1,GPIO, R, LCD, R, 0, ND); +LPC_P(3,2, I2S0_TX_SDA, I2S0_RX_SDA,CAN0,USB1,GPIO, R, LCD, R, 0, ND); +LPC_P(3,3, R, SPI, SSP0, SPIFI, CGU_OUT,R, I2S0_TX_MCLK, I2S1, 0, HS); +LPC_P(3,4, GPIO, R, R, SPIFI, UART1, I2S0_TX_WS, I2S1, LCD, 0, ND); +LPC_P(3,5, GPIO, R, R, SPIFI, UART1, I2S0_TX_SDA,I2S1, LCD, 0, ND); +LPC_P(3,6, GPIO, SPI, SSP0, SPIFI, R, SSP0_ALT, R, R, 0, ND); +LPC_P(3,7, R, SPI, SSP0, SPIFI, GPIO, SSP0_ALT, R, R, 0, ND); +LPC_P(3,8, R, SPI, SSP0, SPIFI, GPIO, SSP0_ALT, R, R, 0, ND); +LPC_P(4,0, GPIO, MCTRL, NMI, R, R, LCD, UART3, R, 0, ND); +LPC_P(4,1, GPIO, CTOUT, LCD, R, R, LCD_ALT, UART3, ENET, ADC0|1, ND); +LPC_P(4,2, GPIO, CTOUT, LCD, R, R, LCD_ALT, UART3, SGPIO, 0, ND); +LPC_P(4,3, GPIO, CTOUT, LCD, R, R, LCD_ALT, UART3, SGPIO, ADC0|0, ND); +LPC_P(4,4, GPIO, CTOUT, LCD, R, R, LCD_ALT, UART3, SGPIO, DAC, ND); +LPC_P(4,5, GPIO, CTOUT, LCD, R, R, R, R, SGPIO, 0, ND); +LPC_P(4,6, GPIO, CTOUT, LCD, R, R, R, R, SGPIO, 0, ND); +LPC_P(4,7, LCD, CLKIN, R, R, R, R, I2S1,I2S0_TX_SCK, 0, ND); +LPC_P(4,8, R, CTIN, LCD, R, GPIO, LCD_ALT, CAN1, SGPIO, 0, ND); +LPC_P(4,9, R, CTIN, LCD, R, GPIO, LCD_ALT, CAN1, SGPIO, 0, ND); +LPC_P(4,10, R, CTIN, LCD, R, GPIO, LCD_ALT, R, SGPIO, 0, ND); +LPC_P(5,0, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,1, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,2, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,3, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,4, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,5, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,6, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(5,7, GPIO, MCTRL, EMC, R, UART1, TIMER1, R, R, 0, ND); +LPC_P(6,0, R, I2S0_RX_MCLK,R, R, I2S0_RX_SCK, R, R, R, 0, ND); +LPC_P(6,1, GPIO, EMC, UART0, I2S0_RX_WS, R, TIMER2, R, R, 0, ND); +LPC_P(6,2, GPIO, EMC, UART0, I2S0_RX_SDA, R, TIMER2, R, R, 0, ND); +LPC_P(6,3, GPIO, USB0, SGPIO, EMC, R, TIMER2, R, R, 0, ND); +LPC_P(6,4, GPIO, CTIN, UART0, EMC, R, R, R, R, 0, ND); +LPC_P(6,5, GPIO, CTOUT, UART0, EMC, R, R, R, R, 0, ND); +LPC_P(6,6, GPIO, EMC, SGPIO, USB0, R, TIMER2, R, R, 0, ND); +LPC_P(6,7, R, EMC, SGPIO, USB0, GPIO, TIMER2, R, R, 0, ND); +LPC_P(6,8, R, EMC, SGPIO, USB0, GPIO, TIMER2, R, R, 0, ND); +LPC_P(6,9, GPIO, R, R, EMC, R, TIMER2, R, R, 0, ND); +LPC_P(6,10, GPIO, MCTRL, R, EMC, R, R, R, R, 0, ND); +LPC_P(6,11, GPIO, R, R, EMC, R, TIMER2, R, R, 0, ND); +LPC_P(6,12, GPIO, CTOUT, R, EMC, R, R, R, R, 0, ND); +LPC_P(7,0, GPIO, CTOUT, R, LCD, R, R, R, SGPIO, 0, ND); +LPC_P(7,1, GPIO, CTOUT,I2S0_TX_WS,LCD,LCD_ALT, R, UART2, SGPIO, 0, ND); +LPC_P(7,2, GPIO, CTIN,I2S0_TX_SDA,LCD,LCD_ALT, R, UART2, SGPIO, 0, ND); +LPC_P(7,3, GPIO, CTIN, R, LCD,LCD_ALT, R, R, R, 0, ND); +LPC_P(7,4, GPIO, CTOUT, R, LCD,LCD_ALT, TRACE, R, R, ADC0|4, ND); +LPC_P(7,5, GPIO, CTOUT, R, LCD,LCD_ALT, TRACE, R, R, ADC0|3, ND); +LPC_P(7,6, GPIO, CTOUT, R, LCD, R, TRACE, R, R, 0, ND); +LPC_P(7,7, GPIO, CTOUT, R, LCD, R, TRACE, ENET, SGPIO, ADC1|6, ND); +LPC_P(8,0, GPIO, USB0, R, MCTRL, SGPIO, R, R, TIMER0, 0, HD); +LPC_P(8,1, GPIO, USB0, R, MCTRL, SGPIO, R, R, TIMER0, 0, HD); +LPC_P(8,2, GPIO, USB0, R, MCTRL, SGPIO, R, R, TIMER0, 0, HD); +LPC_P(8,3, GPIO, USB1, R, LCD, LCD_ALT, R, R, TIMER0, 0, ND); +LPC_P(8,4, GPIO, USB1, R, LCD, LCD_ALT, R, R, TIMER0, 0, ND); +LPC_P(8,5, GPIO, USB1, R, LCD, LCD_ALT, R, R, TIMER0, 0, ND); +LPC_P(8,6, GPIO, USB1, R, LCD, LCD_ALT, R, R, TIMER0, 0, ND); +LPC_P(8,7, GPIO, USB1, R, LCD, LCD_ALT, R, R, TIMER0, 0, ND); +LPC_P(8,8, R, USB1, R, R, R, R,CGU_OUT, I2S1, 0, ND); +LPC_P(9,0, GPIO, MCTRL, R, R, R, ENET, SGPIO, SSP0, 0, ND); +LPC_P(9,1, GPIO, MCTRL, R, R, I2S0_TX_WS,ENET, SGPIO, SSP0, 0, ND); +LPC_P(9,2, GPIO, MCTRL, R, R, I2S0_TX_SDA,ENET,SGPIO, SSP0, 0, ND); +LPC_P(9,3, GPIO, MCTRL, USB1, R, R, ENET, SGPIO, UART3, 0, ND); +LPC_P(9,4, R, MCTRL, USB1, R, GPIO, ENET, SGPIO, UART3, 0, ND); +LPC_P(9,5, R, MCTRL, USB1, R, GPIO, ENET, SGPIO, UART0, 0, ND); +LPC_P(9,6, GPIO, MCTRL, USB1, R, R, ENET, SGPIO, UART0, 0, ND); +LPC_P(a,0, R, R, R, R, R, I2S1, CGU_OUT, R, 0, ND); +LPC_P(a,1, GPIO, QEI, R, UART2, R, R, R, R, 0, HD); +LPC_P(a,2, GPIO, QEI, R, UART2, R, R, R, R, 0, HD); +LPC_P(a,3, GPIO, QEI, R, R, R, R, R, R, 0, HD); +LPC_P(a,4, R, CTOUT, R, EMC, GPIO, R, R, R, 0, ND); +LPC_P(b,0, R, CTOUT, LCD, R, GPIO, R, R, R, 0, ND); +LPC_P(b,1, R, USB1, LCD, R, GPIO, CTOUT, R, R, 0, ND); +LPC_P(b,2, R, USB1, LCD, R, GPIO, CTOUT, R, R, 0, ND); +LPC_P(b,3, R, USB1, LCD, R, GPIO, CTOUT, R, R, 0, ND); +LPC_P(b,4, R, USB1, LCD, R, GPIO, CTIN, R, R, 0, ND); +LPC_P(b,5, R, USB1, LCD, R, GPIO, CTIN, LCD_ALT, R, 0, ND); +LPC_P(b,6, R, USB1, LCD, R, GPIO, CTIN, LCD_ALT, R, ADC0|6, ND); +LPC_P(c,0, R, USB1, R, ENET, LCD, R, R, SDMMC, ADC1|1, ND); +LPC_P(c,1, USB1, R, UART1, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,2, USB1, R, UART1, ENET, GPIO, R, R, SDMMC, 0, ND); +LPC_P(c,3, USB1, R, UART1, ENET, GPIO, R, R, SDMMC, ADC1|0, ND); +LPC_P(c,4, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,5, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,6, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,7, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,8, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,9, R, USB1, R, ENET, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,10, R, USB1, UART1, R, GPIO, R, TIMER3, SDMMC, 0, ND); +LPC_P(c,11, R, USB1, UART1, R, GPIO, R, R, SDMMC, 0, ND); +LPC_P(c,12, R, R, UART1, R, GPIO, SGPIO, I2S0_TX_SDA,SDMMC, 0, ND); +LPC_P(c,13, R, R, UART1, R, GPIO, SGPIO, I2S0_TX_WS, SDMMC, 0, ND); +LPC_P(c,14, R, R, UART1, R, GPIO, SGPIO, ENET, SDMMC, 0, ND); +LPC_P(d,0, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,1, R, R, EMC, R, GPIO, SDMMC, R, SGPIO, 0, ND); +LPC_P(d,2, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,3, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,4, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,5, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,6, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,7, R, CTIN, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,8, R, CTIN, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,9, R, CTOUT, EMC, R, GPIO, R, R, SGPIO, 0, ND); +LPC_P(d,10, R, CTIN, EMC, R, GPIO, R, R, R, 0, ND); +LPC_P(d,11, R, R, EMC, R, GPIO, USB1, CTOUT, R, 0, ND); +LPC_P(d,12, R, R, EMC, R, GPIO, R, CTOUT, R, 0, ND); +LPC_P(d,13, R, CTIN, EMC, R, GPIO, R, CTOUT, R, 0, ND); +LPC_P(d,14, R, R, EMC, R, GPIO, R, CTOUT, R, 0, ND); +LPC_P(d,15, R, R, EMC, R, GPIO, SDMMC, CTOUT, R, 0, ND); +LPC_P(d,16, R, R, EMC, R, GPIO, SDMMC, CTOUT, R, 0, ND); +LPC_P(e,0, R, R, R, EMC, GPIO, CAN1, R, R, 0, ND); +LPC_P(e,1, R, R, R, EMC, GPIO, CAN1, R, R, 0, ND); +LPC_P(e,2,ADCTRIG, CAN0, R, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,3, R, CAN0,ADCTRIG, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,4, R, NMI, R, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,5, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,6, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,7, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,8, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,9, R, CTIN, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,10, R, CTIN, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,11, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,12, R, CTOUT, UART1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,13, R, CTOUT, I2C1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,14, R, R, R, EMC, GPIO, R, R, R, 0, ND); +LPC_P(e,15, R, CTOUT, I2C1, EMC, GPIO, R, R, R, 0, ND); +LPC_P(f,0, SSP0, CLKIN, R, R, R, R, R, I2S1, 0, ND); +LPC_P(f,1, R, R, SSP0, R, GPIO, R, SGPIO, R, 0, ND); +LPC_P(f,2, R, UART3, SSP0, R, GPIO, R, SGPIO, R, 0, ND); +LPC_P(f,3, R, UART3, SSP0, R, GPIO, R, SGPIO, R, 0, ND); +LPC_P(f,4, SSP1, CLKIN, TRACE, R, R, R, I2S0_TX_MCLK,I2S0_RX_SCK, 0, ND); +LPC_P(f,5, R, UART3, SSP1, TRACE, GPIO, R, SGPIO, R, ADC1|4, ND); +LPC_P(f,6, R, UART3, SSP1, TRACE, GPIO, R, SGPIO, I2S1, ADC1|3, ND); +LPC_P(f,7, R, UART3, SSP1, TRACE, GPIO, R, SGPIO, I2S1, ADC1|7, ND); +LPC_P(f,8, R, UART0, CTIN, TRACE, GPIO, R, SGPIO, R, ADC0|2, ND); +LPC_P(f,9, R, UART0, CTOUT, R, GPIO, R, SGPIO, R, ADC1|2, ND); +LPC_P(f,10, R, UART0, R, R, GPIO, R, SDMMC, R, ADC0|5, ND); +LPC_P(f,11, R, UART0, R, R, GPIO, R, SDMMC, R, ADC1|5, ND); + +/* Pin Offset FUNC0 FUNC1 FUNC2 FUNC3 FUNC4 FUNC5 FUNC6 FUNC7 ANALOG TYPE */ +LPC_N(clk0, 0xc00, EMC, CLKOUT, R, R, SDMMC, EMC_ALT, SSP1, ENET, 0, HS); +LPC_N(clk1, 0xc04, EMC, CLKOUT, R, R, R, CGU_OUT, R, I2S1, 0, HS); +LPC_N(clk2, 0xc08, EMC, CLKOUT, R, R, SDMMC, EMC_ALT,I2S0_TX_MCLK,I2S1, 0, HS); +LPC_N(clk3, 0xc0c, EMC, CLKOUT, R, R, R, CGU_OUT, R, I2S1, 0, HS); +LPC_N(usb1_dm, 0xc80, R, R, R, R, R, R, R, R, 0, USB1); +LPC_N(usb1_dp, 0xc80, R, R, R, R, R, R, R, R, 0, USB1); +LPC_N(i2c0_scl, 0xc84, R, R, R, R, R, R, R, R, 0, I2C0); +LPC_N(i2c0_sda, 0xc84, R, R, R, R, R, R, R, R, 0, I2C0); + +#define LPC18XX_PIN_P(port, pin) { \ + .number = 0x##port * 32 + pin, \ + .name = "p"#port"_"#pin, \ + .drv_data = &lpc18xx_pin_p##port##_##pin \ +} + +/* Pin numbers for special pins */ +enum { + PIN_CLK0 = 600, + PIN_CLK1, + PIN_CLK2, + PIN_CLK3, + PIN_USB1_DM, + PIN_USB1_DP, + PIN_I2C0_SCL, + PIN_I2C0_SDA, +}; + +#define LPC18XX_PIN(pname, n) { \ + .number = n, \ + .name = #pname, \ + .drv_data = &lpc18xx_pin_##pname \ +} + +static const struct pinctrl_pin_desc lpc18xx_pins[] = { + LPC18XX_PIN_P(0,0), + LPC18XX_PIN_P(0,1), + LPC18XX_PIN_P(1,0), + LPC18XX_PIN_P(1,1), + LPC18XX_PIN_P(1,2), + LPC18XX_PIN_P(1,3), + LPC18XX_PIN_P(1,4), + LPC18XX_PIN_P(1,5), + LPC18XX_PIN_P(1,6), + LPC18XX_PIN_P(1,7), + LPC18XX_PIN_P(1,8), + LPC18XX_PIN_P(1,9), + LPC18XX_PIN_P(1,10), + LPC18XX_PIN_P(1,11), + LPC18XX_PIN_P(1,12), + LPC18XX_PIN_P(1,13), + LPC18XX_PIN_P(1,14), + LPC18XX_PIN_P(1,15), + LPC18XX_PIN_P(1,16), + LPC18XX_PIN_P(1,17), + LPC18XX_PIN_P(1,18), + LPC18XX_PIN_P(1,19), + LPC18XX_PIN_P(1,20), + LPC18XX_PIN_P(2,0), + LPC18XX_PIN_P(2,1), + LPC18XX_PIN_P(2,2), + LPC18XX_PIN_P(2,3), + LPC18XX_PIN_P(2,4), + LPC18XX_PIN_P(2,5), + LPC18XX_PIN_P(2,6), + LPC18XX_PIN_P(2,7), + LPC18XX_PIN_P(2,8), + LPC18XX_PIN_P(2,9), + LPC18XX_PIN_P(2,10), + LPC18XX_PIN_P(2,11), + LPC18XX_PIN_P(2,12), + LPC18XX_PIN_P(2,13), + LPC18XX_PIN_P(3,0), + LPC18XX_PIN_P(3,1), + LPC18XX_PIN_P(3,2), + LPC18XX_PIN_P(3,3), + LPC18XX_PIN_P(3,4), + LPC18XX_PIN_P(3,5), + LPC18XX_PIN_P(3,6), + LPC18XX_PIN_P(3,7), + LPC18XX_PIN_P(3,8), + LPC18XX_PIN_P(4,0), + LPC18XX_PIN_P(4,1), + LPC18XX_PIN_P(4,2), + LPC18XX_PIN_P(4,3), + LPC18XX_PIN_P(4,4), + LPC18XX_PIN_P(4,5), + LPC18XX_PIN_P(4,6), + LPC18XX_PIN_P(4,7), + LPC18XX_PIN_P(4,8), + LPC18XX_PIN_P(4,9), + LPC18XX_PIN_P(4,10), + LPC18XX_PIN_P(5,0), + LPC18XX_PIN_P(5,1), + LPC18XX_PIN_P(5,2), + LPC18XX_PIN_P(5,3), + LPC18XX_PIN_P(5,4), + LPC18XX_PIN_P(5,5), + LPC18XX_PIN_P(5,6), + LPC18XX_PIN_P(5,7), + LPC18XX_PIN_P(6,0), + LPC18XX_PIN_P(6,1), + LPC18XX_PIN_P(6,2), + LPC18XX_PIN_P(6,3), + LPC18XX_PIN_P(6,4), + LPC18XX_PIN_P(6,5), + LPC18XX_PIN_P(6,6), + LPC18XX_PIN_P(6,7), + LPC18XX_PIN_P(6,8), + LPC18XX_PIN_P(6,9), + LPC18XX_PIN_P(6,10), + LPC18XX_PIN_P(6,11), + LPC18XX_PIN_P(6,12), + LPC18XX_PIN_P(7,0), + LPC18XX_PIN_P(7,1), + LPC18XX_PIN_P(7,2), + LPC18XX_PIN_P(7,3), + LPC18XX_PIN_P(7,4), + LPC18XX_PIN_P(7,5), + LPC18XX_PIN_P(7,6), + LPC18XX_PIN_P(7,7), + LPC18XX_PIN_P(8,0), + LPC18XX_PIN_P(8,1), + LPC18XX_PIN_P(8,2), + LPC18XX_PIN_P(8,3), + LPC18XX_PIN_P(8,4), + LPC18XX_PIN_P(8,5), + LPC18XX_PIN_P(8,6), + LPC18XX_PIN_P(8,7), + LPC18XX_PIN_P(8,8), + LPC18XX_PIN_P(9,0), + LPC18XX_PIN_P(9,1), + LPC18XX_PIN_P(9,2), + LPC18XX_PIN_P(9,3), + LPC18XX_PIN_P(9,4), + LPC18XX_PIN_P(9,5), + LPC18XX_PIN_P(9,6), + LPC18XX_PIN_P(a,0), + LPC18XX_PIN_P(a,1), + LPC18XX_PIN_P(a,2), + LPC18XX_PIN_P(a,3), + LPC18XX_PIN_P(a,4), + LPC18XX_PIN_P(b,0), + LPC18XX_PIN_P(b,1), + LPC18XX_PIN_P(b,2), + LPC18XX_PIN_P(b,3), + LPC18XX_PIN_P(b,4), + LPC18XX_PIN_P(b,5), + LPC18XX_PIN_P(b,6), + LPC18XX_PIN_P(c,0), + LPC18XX_PIN_P(c,1), + LPC18XX_PIN_P(c,2), + LPC18XX_PIN_P(c,3), + LPC18XX_PIN_P(c,4), + LPC18XX_PIN_P(c,5), + LPC18XX_PIN_P(c,6), + LPC18XX_PIN_P(c,7), + LPC18XX_PIN_P(c,8), + LPC18XX_PIN_P(c,9), + LPC18XX_PIN_P(c,10), + LPC18XX_PIN_P(c,11), + LPC18XX_PIN_P(c,12), + LPC18XX_PIN_P(c,13), + LPC18XX_PIN_P(c,14), + LPC18XX_PIN_P(d,0), + LPC18XX_PIN_P(d,1), + LPC18XX_PIN_P(d,2), + LPC18XX_PIN_P(d,3), + LPC18XX_PIN_P(d,4), + LPC18XX_PIN_P(d,5), + LPC18XX_PIN_P(d,6), + LPC18XX_PIN_P(d,7), + LPC18XX_PIN_P(d,8), + LPC18XX_PIN_P(d,9), + LPC18XX_PIN_P(d,10), + LPC18XX_PIN_P(d,11), + LPC18XX_PIN_P(d,12), + LPC18XX_PIN_P(d,13), + LPC18XX_PIN_P(d,14), + LPC18XX_PIN_P(d,15), + LPC18XX_PIN_P(d,16), + LPC18XX_PIN_P(e,0), + LPC18XX_PIN_P(e,1), + LPC18XX_PIN_P(e,2), + LPC18XX_PIN_P(e,3), + LPC18XX_PIN_P(e,4), + LPC18XX_PIN_P(e,5), + LPC18XX_PIN_P(e,6), + LPC18XX_PIN_P(e,7), + LPC18XX_PIN_P(e,8), + LPC18XX_PIN_P(e,9), + LPC18XX_PIN_P(e,10), + LPC18XX_PIN_P(e,11), + LPC18XX_PIN_P(e,12), + LPC18XX_PIN_P(e,13), + LPC18XX_PIN_P(e,14), + LPC18XX_PIN_P(e,15), + LPC18XX_PIN_P(f,0), + LPC18XX_PIN_P(f,1), + LPC18XX_PIN_P(f,2), + LPC18XX_PIN_P(f,3), + LPC18XX_PIN_P(f,4), + LPC18XX_PIN_P(f,5), + LPC18XX_PIN_P(f,6), + LPC18XX_PIN_P(f,7), + LPC18XX_PIN_P(f,8), + LPC18XX_PIN_P(f,9), + LPC18XX_PIN_P(f,10), + LPC18XX_PIN_P(f,11), + + LPC18XX_PIN(clk0, PIN_CLK0), + LPC18XX_PIN(clk1, PIN_CLK1), + LPC18XX_PIN(clk2, PIN_CLK2), + LPC18XX_PIN(clk3, PIN_CLK3), + LPC18XX_PIN(usb1_dm, PIN_USB1_DM), + LPC18XX_PIN(usb1_dp, PIN_USB1_DP), + LPC18XX_PIN(i2c0_scl, PIN_I2C0_SCL), + LPC18XX_PIN(i2c0_sda, PIN_I2C0_SDA), +}; + +/* PIN_CONFIG_GPIO_PIN_INT: route gpio to the gpio pin interrupt controller */ +#define PIN_CONFIG_GPIO_PIN_INT (PIN_CONFIG_END + 1) + +static const struct pinconf_generic_params lpc18xx_params[] = { + {"nxp,gpio-pin-interrupt", PIN_CONFIG_GPIO_PIN_INT, 0}, +}; + +#ifdef CONFIG_DEBUG_FS +static const struct pin_config_item lpc18xx_conf_items[ARRAY_SIZE(lpc18xx_params)] = { + PCONFDUMP(PIN_CONFIG_GPIO_PIN_INT, "gpio pin int", NULL, true), +}; +#endif + +static int lpc18xx_pconf_get_usb1(enum pin_config_param param, int *arg, u32 reg) +{ + switch (param) { + case PIN_CONFIG_MODE_LOW_POWER: + if (reg & LPC18XX_SCU_USB1_EPWR) + *arg = 0; + else + *arg = 1; + break; + + case PIN_CONFIG_BIAS_DISABLE: + if (reg & LPC18XX_SCU_USB1_EPD) + return -EINVAL; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + if (reg & LPC18XX_SCU_USB1_EPD) + *arg = 1; + else + return -EINVAL; + break; + + default: + return -ENOTSUPP; + } + + return 0; +} + +static int lpc18xx_pconf_get_i2c0(enum pin_config_param param, int *arg, u32 reg, + unsigned pin) +{ + u8 shift; + + if (pin == PIN_I2C0_SCL) + shift = LPC18XX_SCU_I2C0_SCL_SHIFT; + else + shift = LPC18XX_SCU_I2C0_SDA_SHIFT; + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (reg & (LPC18XX_SCU_I2C0_EZI << shift)) + *arg = 1; + else + return -EINVAL; + break; + + case PIN_CONFIG_SLEW_RATE: + if (reg & (LPC18XX_SCU_I2C0_EHD << shift)) + *arg = 1; + else + *arg = 0; + break; + + case PIN_CONFIG_INPUT_SCHMITT: + if (reg & (LPC18XX_SCU_I2C0_EFP << shift)) + *arg = 3; + else + *arg = 50; + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (reg & (LPC18XX_SCU_I2C0_ZIF << shift)) + return -EINVAL; + else + *arg = 1; + break; + + default: + return -ENOTSUPP; + } + + return 0; +} + +static int lpc18xx_pin_to_gpio(struct pinctrl_dev *pctldev, unsigned pin) +{ + struct pinctrl_gpio_range *range; + + range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); + if (!range) + return -EINVAL; + + return pin - range->pin_base + range->base; +} + +static int lpc18xx_get_pintsel(void __iomem *addr, u32 val, int *arg) +{ + u32 reg_val; + int i; + + reg_val = readl(addr); + for (i = 0; i < LPC18XX_SCU_IRQ_PER_PINTSEL; i++) { + if ((reg_val & LPC18XX_SCU_PINTSEL_VAL_MASK) == val) + return 0; + + reg_val >>= BITS_PER_BYTE; + *arg += 1; + } + + return -EINVAL; +} + +static u32 lpc18xx_gpio_to_pintsel_val(int gpio) +{ + unsigned int gpio_port, gpio_pin; + + gpio_port = gpio / LPC18XX_GPIO_PINS_PER_PORT; + gpio_pin = gpio % LPC18XX_GPIO_PINS_PER_PORT; + + return gpio_pin | (gpio_port << LPC18XX_SCU_PINTSEL_PORT_SHIFT); +} + +static int lpc18xx_pconf_get_gpio_pin_int(struct pinctrl_dev *pctldev, + int *arg, unsigned pin) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + int gpio, ret; + u32 val; + + gpio = lpc18xx_pin_to_gpio(pctldev, pin); + if (gpio < 0) + return -ENOTSUPP; + + val = lpc18xx_gpio_to_pintsel_val(gpio); + + /* + * Check if this pin has been enabled as a interrupt in any of the two + * PINTSEL registers. *arg indicates which interrupt number (0-7). + */ + *arg = 0; + ret = lpc18xx_get_pintsel(scu->base + LPC18XX_SCU_PINTSEL0, val, arg); + if (ret == 0) + return ret; + + return lpc18xx_get_pintsel(scu->base + LPC18XX_SCU_PINTSEL1, val, arg); +} + +static int lpc18xx_pconf_get_pin(struct pinctrl_dev *pctldev, unsigned param, + int *arg, u32 reg, unsigned pin, + struct lpc18xx_pin_caps *pin_cap) +{ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if ((!(reg & LPC18XX_SCU_PIN_EPD)) && (reg & LPC18XX_SCU_PIN_EPUN)) + ; + else + return -EINVAL; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + if (reg & LPC18XX_SCU_PIN_EPUN) + return -EINVAL; + else + *arg = 1; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + if (reg & LPC18XX_SCU_PIN_EPD) + *arg = 1; + else + return -EINVAL; + break; + + case PIN_CONFIG_INPUT_ENABLE: + if (reg & LPC18XX_SCU_PIN_EZI) + *arg = 1; + else + return -EINVAL; + break; + + case PIN_CONFIG_SLEW_RATE: + if (pin_cap->type == TYPE_HD) + return -ENOTSUPP; + + if (reg & LPC18XX_SCU_PIN_EHS) + *arg = 1; + else + *arg = 0; + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (reg & LPC18XX_SCU_PIN_ZIF) + return -EINVAL; + else + *arg = 1; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + if (pin_cap->type != TYPE_HD) + return -ENOTSUPP; + + *arg = (reg & LPC18XX_SCU_PIN_EHD_MASK) >> LPC18XX_SCU_PIN_EHD_POS; + switch (*arg) { + case 3: *arg += 5; + fallthrough; + case 2: *arg += 5; + fallthrough; + case 1: *arg += 3; + fallthrough; + case 0: *arg += 4; + } + break; + + case PIN_CONFIG_GPIO_PIN_INT: + return lpc18xx_pconf_get_gpio_pin_int(pctldev, arg, pin); + + default: + return -ENOTSUPP; + } + + return 0; +} + +static struct lpc18xx_pin_caps *lpc18xx_get_pin_caps(unsigned pin) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lpc18xx_pins); i++) { + if (lpc18xx_pins[i].number == pin) + return lpc18xx_pins[i].drv_data; + } + + return NULL; +} + +static int lpc18xx_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + struct lpc18xx_pin_caps *pin_cap; + int ret, arg = 0; + u32 reg; + + pin_cap = lpc18xx_get_pin_caps(pin); + if (!pin_cap) + return -EINVAL; + + reg = readl(scu->base + pin_cap->offset); + + if (pin_cap->type == TYPE_I2C0) + ret = lpc18xx_pconf_get_i2c0(param, &arg, reg, pin); + else if (pin_cap->type == TYPE_USB1) + ret = lpc18xx_pconf_get_usb1(param, &arg, reg); + else + ret = lpc18xx_pconf_get_pin(pctldev, param, &arg, reg, pin, pin_cap); + + if (ret < 0) + return ret; + + *config = pinconf_to_config_packed(param, (u16)arg); + + return 0; +} + +static int lpc18xx_pconf_set_usb1(struct pinctrl_dev *pctldev, + enum pin_config_param param, + u32 param_val, u32 *reg) +{ + switch (param) { + case PIN_CONFIG_MODE_LOW_POWER: + if (param_val) + *reg &= ~LPC18XX_SCU_USB1_EPWR; + else + *reg |= LPC18XX_SCU_USB1_EPWR; + break; + + case PIN_CONFIG_BIAS_DISABLE: + *reg &= ~LPC18XX_SCU_USB1_EPD; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + *reg |= LPC18XX_SCU_USB1_EPD; + break; + + default: + dev_err(pctldev->dev, "Property not supported\n"); + return -ENOTSUPP; + } + + return 0; +} + +static int lpc18xx_pconf_set_i2c0(struct pinctrl_dev *pctldev, + enum pin_config_param param, + u32 param_val, u32 *reg, + unsigned pin) +{ + u8 shift; + + if (pin == PIN_I2C0_SCL) + shift = LPC18XX_SCU_I2C0_SCL_SHIFT; + else + shift = LPC18XX_SCU_I2C0_SDA_SHIFT; + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (param_val) + *reg |= (LPC18XX_SCU_I2C0_EZI << shift); + else + *reg &= ~(LPC18XX_SCU_I2C0_EZI << shift); + break; + + case PIN_CONFIG_SLEW_RATE: + if (param_val) + *reg |= (LPC18XX_SCU_I2C0_EHD << shift); + else + *reg &= ~(LPC18XX_SCU_I2C0_EHD << shift); + break; + + case PIN_CONFIG_INPUT_SCHMITT: + if (param_val == 3) + *reg |= (LPC18XX_SCU_I2C0_EFP << shift); + else if (param_val == 50) + *reg &= ~(LPC18XX_SCU_I2C0_EFP << shift); + else + return -ENOTSUPP; + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (param_val) + *reg &= ~(LPC18XX_SCU_I2C0_ZIF << shift); + else + *reg |= (LPC18XX_SCU_I2C0_ZIF << shift); + break; + + default: + dev_err(pctldev->dev, "Property not supported\n"); + return -ENOTSUPP; + } + + return 0; +} + +static int lpc18xx_pconf_set_gpio_pin_int(struct pinctrl_dev *pctldev, + u32 param_val, unsigned pin) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + u32 val, reg_val, reg_offset = LPC18XX_SCU_PINTSEL0; + int gpio; + + if (param_val >= LPC18XX_GPIO_PIN_INT_MAX) + return -EINVAL; + + gpio = lpc18xx_pin_to_gpio(pctldev, pin); + if (gpio < 0) + return -ENOTSUPP; + + val = lpc18xx_gpio_to_pintsel_val(gpio); + + reg_offset += (param_val / LPC18XX_SCU_IRQ_PER_PINTSEL) * sizeof(u32); + + reg_val = readl(scu->base + reg_offset); + reg_val &= ~LPC18XX_SCU_PINTSEL_VAL(LPC18XX_SCU_PINTSEL_VAL_MASK, param_val); + reg_val |= LPC18XX_SCU_PINTSEL_VAL(val, param_val); + writel(reg_val, scu->base + reg_offset); + + return 0; +} + +static int lpc18xx_pconf_set_pin(struct pinctrl_dev *pctldev, unsigned param, + u32 param_val, u32 *reg, unsigned pin, + struct lpc18xx_pin_caps *pin_cap) +{ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + *reg &= ~LPC18XX_SCU_PIN_EPD; + *reg |= LPC18XX_SCU_PIN_EPUN; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + *reg &= ~LPC18XX_SCU_PIN_EPUN; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + *reg |= LPC18XX_SCU_PIN_EPD; + break; + + case PIN_CONFIG_INPUT_ENABLE: + if (param_val) + *reg |= LPC18XX_SCU_PIN_EZI; + else + *reg &= ~LPC18XX_SCU_PIN_EZI; + break; + + case PIN_CONFIG_SLEW_RATE: + if (pin_cap->type == TYPE_HD) { + dev_err(pctldev->dev, "Slew rate unsupported on high-drive pins\n"); + return -ENOTSUPP; + } + + if (param_val == 0) + *reg &= ~LPC18XX_SCU_PIN_EHS; + else + *reg |= LPC18XX_SCU_PIN_EHS; + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + if (param_val) + *reg &= ~LPC18XX_SCU_PIN_ZIF; + else + *reg |= LPC18XX_SCU_PIN_ZIF; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + if (pin_cap->type != TYPE_HD) { + dev_err(pctldev->dev, "Drive strength available only on high-drive pins\n"); + return -ENOTSUPP; + } + *reg &= ~LPC18XX_SCU_PIN_EHD_MASK; + + switch (param_val) { + case 20: param_val -= 5; + fallthrough; + case 14: param_val -= 5; + fallthrough; + case 8: param_val -= 3; + fallthrough; + case 4: param_val -= 4; + break; + default: + dev_err(pctldev->dev, "Drive strength %u unsupported\n", param_val); + return -ENOTSUPP; + } + *reg |= param_val << LPC18XX_SCU_PIN_EHD_POS; + break; + + case PIN_CONFIG_GPIO_PIN_INT: + return lpc18xx_pconf_set_gpio_pin_int(pctldev, param_val, pin); + + default: + dev_err(pctldev->dev, "Property not supported\n"); + return -ENOTSUPP; + } + + return 0; +} + +static int lpc18xx_pconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + struct lpc18xx_pin_caps *pin_cap; + enum pin_config_param param; + u32 param_val; + u32 reg; + int ret; + int i; + + pin_cap = lpc18xx_get_pin_caps(pin); + if (!pin_cap) + return -EINVAL; + + reg = readl(scu->base + pin_cap->offset); + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + param_val = pinconf_to_config_argument(configs[i]); + + if (pin_cap->type == TYPE_I2C0) + ret = lpc18xx_pconf_set_i2c0(pctldev, param, param_val, ®, pin); + else if (pin_cap->type == TYPE_USB1) + ret = lpc18xx_pconf_set_usb1(pctldev, param, param_val, ®); + else + ret = lpc18xx_pconf_set_pin(pctldev, param, param_val, ®, pin, pin_cap); + + if (ret) + return ret; + } + + writel(reg, scu->base + pin_cap->offset); + + return 0; +} + +static const struct pinconf_ops lpc18xx_pconf_ops = { + .is_generic = true, + .pin_config_get = lpc18xx_pconf_get, + .pin_config_set = lpc18xx_pconf_set, +}; + +static int lpc18xx_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(lpc18xx_function_names); +} + +static const char *lpc18xx_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + return lpc18xx_function_names[function]; +} + +static int lpc18xx_pmx_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char *const **groups, + unsigned *const num_groups) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + + *groups = scu->func[function].groups; + *num_groups = scu->func[function].ngroups; + + return 0; +} + +static int lpc18xx_pmx_set(struct pinctrl_dev *pctldev, unsigned function, + unsigned group) +{ + struct lpc18xx_scu_data *scu = pinctrl_dev_get_drvdata(pctldev); + struct lpc18xx_pin_caps *pin = lpc18xx_pins[group].drv_data; + int func; + u32 reg; + + /* Dedicated USB1 and I2C0 pins doesn't support muxing */ + if (pin->type == TYPE_USB1) { + if (function == FUNC_USB1) + return 0; + + goto fail; + } + + if (pin->type == TYPE_I2C0) { + if (function == FUNC_I2C0) + return 0; + + goto fail; + } + + if (function == FUNC_ADC && (pin->analog & LPC18XX_ANALOG_PIN)) { + u32 offset; + + writel(LPC18XX_SCU_ANALOG_PIN_CFG, scu->base + pin->offset); + + if (LPC18XX_ANALOG_ADC(pin->analog) == 0) + offset = LPC18XX_SCU_REG_ENAIO0; + else + offset = LPC18XX_SCU_REG_ENAIO1; + + reg = readl(scu->base + offset); + reg |= pin->analog & LPC18XX_ANALOG_BIT_MASK; + writel(reg, scu->base + offset); + + return 0; + } + + if (function == FUNC_DAC && (pin->analog & LPC18XX_ANALOG_PIN)) { + writel(LPC18XX_SCU_ANALOG_PIN_CFG, scu->base + pin->offset); + + reg = readl(scu->base + LPC18XX_SCU_REG_ENAIO2); + reg |= LPC18XX_SCU_REG_ENAIO2_DAC; + writel(reg, scu->base + LPC18XX_SCU_REG_ENAIO2); + + return 0; + } + + for (func = 0; func < LPC18XX_SCU_FUNC_PER_PIN; func++) { + if (function == pin->functions[func]) + break; + } + + if (func >= LPC18XX_SCU_FUNC_PER_PIN) + goto fail; + + reg = readl(scu->base + pin->offset); + reg &= ~LPC18XX_SCU_PIN_MODE_MASK; + writel(reg | func, scu->base + pin->offset); + + return 0; +fail: + dev_err(pctldev->dev, "Pin %s can't be %s\n", lpc18xx_pins[group].name, + lpc18xx_function_names[function]); + return -EINVAL; +} + +static const struct pinmux_ops lpc18xx_pmx_ops = { + .get_functions_count = lpc18xx_pmx_get_funcs_count, + .get_function_name = lpc18xx_pmx_get_func_name, + .get_function_groups = lpc18xx_pmx_get_func_groups, + .set_mux = lpc18xx_pmx_set, +}; + +static int lpc18xx_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(lpc18xx_pins); +} + +static const char *lpc18xx_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + return lpc18xx_pins[group].name; +} + +static int lpc18xx_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + *pins = &lpc18xx_pins[group].number; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops lpc18xx_pctl_ops = { + .get_groups_count = lpc18xx_pctl_get_groups_count, + .get_group_name = lpc18xx_pctl_get_group_name, + .get_group_pins = lpc18xx_pctl_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_free_map, +}; + +static struct pinctrl_desc lpc18xx_scu_desc = { + .name = "lpc18xx/43xx-scu", + .pins = lpc18xx_pins, + .npins = ARRAY_SIZE(lpc18xx_pins), + .pctlops = &lpc18xx_pctl_ops, + .pmxops = &lpc18xx_pmx_ops, + .confops = &lpc18xx_pconf_ops, + .num_custom_params = ARRAY_SIZE(lpc18xx_params), + .custom_params = lpc18xx_params, +#ifdef CONFIG_DEBUG_FS + .custom_conf_items = lpc18xx_conf_items, +#endif + .owner = THIS_MODULE, +}; + +static bool lpc18xx_valid_pin_function(unsigned pin, unsigned function) +{ + struct lpc18xx_pin_caps *p = lpc18xx_pins[pin].drv_data; + int i; + + if (function == FUNC_DAC && p->analog == DAC) + return true; + + if (function == FUNC_ADC && p->analog) + return true; + + if (function == FUNC_I2C0 && p->type == TYPE_I2C0) + return true; + + if (function == FUNC_USB1 && p->type == TYPE_USB1) + return true; + + for (i = 0; i < LPC18XX_SCU_FUNC_PER_PIN; i++) { + if (function == p->functions[i]) + return true; + } + + return false; +} + +static int lpc18xx_create_group_func_map(struct device *dev, + struct lpc18xx_scu_data *scu) +{ + u16 pins[ARRAY_SIZE(lpc18xx_pins)]; + int func, ngroups, i; + + for (func = 0; func < FUNC_MAX; func++) { + for (ngroups = 0, i = 0; i < ARRAY_SIZE(lpc18xx_pins); i++) { + if (lpc18xx_valid_pin_function(i, func)) + pins[ngroups++] = i; + } + + scu->func[func].ngroups = ngroups; + scu->func[func].groups = devm_kcalloc(dev, + ngroups, sizeof(char *), + GFP_KERNEL); + if (!scu->func[func].groups) + return -ENOMEM; + + for (i = 0; i < ngroups; i++) + scu->func[func].groups[i] = lpc18xx_pins[pins[i]].name; + } + + return 0; +} + +static int lpc18xx_scu_probe(struct platform_device *pdev) +{ + struct lpc18xx_scu_data *scu; + int ret; + + scu = devm_kzalloc(&pdev->dev, sizeof(*scu), GFP_KERNEL); + if (!scu) + return -ENOMEM; + + scu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(scu->base)) + return PTR_ERR(scu->base); + + scu->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(scu->clk)) { + dev_err(&pdev->dev, "Input clock not found.\n"); + return PTR_ERR(scu->clk); + } + + ret = lpc18xx_create_group_func_map(&pdev->dev, scu); + if (ret) { + dev_err(&pdev->dev, "Unable to create group func map.\n"); + return ret; + } + + ret = clk_prepare_enable(scu->clk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable clock.\n"); + return ret; + } + + platform_set_drvdata(pdev, scu); + + scu->pctl = devm_pinctrl_register(&pdev->dev, &lpc18xx_scu_desc, scu); + if (IS_ERR(scu->pctl)) { + dev_err(&pdev->dev, "Could not register pinctrl driver\n"); + clk_disable_unprepare(scu->clk); + return PTR_ERR(scu->pctl); + } + + return 0; +} + +static const struct of_device_id lpc18xx_scu_match[] = { + { .compatible = "nxp,lpc1850-scu" }, + {}, +}; + +static struct platform_driver lpc18xx_scu_driver = { + .probe = lpc18xx_scu_probe, + .driver = { + .name = "lpc18xx-scu", + .of_match_table = lpc18xx_scu_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(lpc18xx_scu_driver); |