1 // SPDX-License-Identifier: GPL-2.0-only
3 * CPLD driver for the MikroTik RouterBoard 4xx series
5 * This driver provides access to a CPLD that interfaces between the SoC SPI bus
6 * and other devices. Behind the CPLD there is a NAND flash chip and five LEDs.
8 * The CPLD supports SPI two-wire mode, in which two bits are transferred per
9 * SPI clock cycle. The second bit is transmitted with the SoC's CS2 pin.
11 * The CPLD also acts as a GPIO expander.
13 * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
14 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
15 * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
16 * Copyright (C) 2020 Christopher Hill <ch6574@gmail.com>
18 * This file was based on the driver for Linux 2.6.22 published by
19 * MikroTik for their RouterBoard 4xx series devices.
21 #include <linux/mfd/core.h>
22 #include <linux/spi/spi.h>
23 #include <linux/module.h>
24 #include <linux/of_platform.h>
26 #include <mfd/rb4xx-cpld.h>
29 #define CPLD_CMD_WRITE_NAND 0x08 /* send cmd, n x send data, send idle */
30 #define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */
31 #define CPLD_CMD_READ_NAND 0x0a /* send cmd, send idle, n x read data */
32 #define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */
33 #define CPLD_CMD_GPIO8_HIGH 0x0c /* send cmd */
34 #define CPLD_CMD_GPIO8_LOW 0x0d /* send cmd */
36 static int rb4xx_cpld_write_nand(struct rb4xx_cpld
*cpld
, const void *tx_buf
,
40 static const u8 cmd
= CPLD_CMD_WRITE_NAND
;
41 struct spi_transfer t
[3] = {
48 .tx_nbits
= SPI_NBITS_DUAL
,
51 .tx_nbits
= SPI_NBITS_DUAL
,
56 spi_message_add_tail(&t
[0], &m
);
57 spi_message_add_tail(&t
[1], &m
);
58 spi_message_add_tail(&t
[2], &m
);
59 return spi_sync(cpld
->spi
, &m
);
62 static int rb4xx_cpld_read_nand(struct rb4xx_cpld
*cpld
, void *rx_buf
,
66 static const u8 cmd
[2] = {
69 struct spi_transfer t
[2] = {
80 spi_message_add_tail(&t
[0], &m
);
81 spi_message_add_tail(&t
[1], &m
);
82 return spi_sync(cpld
->spi
, &m
);
85 static int rb4xx_cpld_cmd(struct rb4xx_cpld
*cpld
, const void *tx_buf
,
89 struct spi_transfer t
= {
95 spi_message_add_tail(&t
, &m
);
96 return spi_sync(cpld
->spi
, &m
);
99 static int rb4xx_cpld_gpio_set_0_7(struct rb4xx_cpld
*cpld
, u8 values
)
101 /* GPIO 0-7 change can be sent via command + bitfield */
103 CPLD_CMD_WRITE_CFG
, values
105 return rb4xx_cpld_cmd(cpld
, &cmd
, 2);
108 static int rb4xx_cpld_gpio_set_8(struct rb4xx_cpld
*cpld
, u8 value
)
110 /* GPIO 8 uses dedicated high/low commands */
111 u8 cmd
= CPLD_CMD_GPIO8_HIGH
| !!(value
);
112 return rb4xx_cpld_cmd(cpld
, &cmd
, 1);
115 static const struct mfd_cell rb4xx_cpld_cells
[] = {
117 .name
= "mikrotik,rb4xx-gpio",
118 .of_compatible
= "mikrotik,rb4xx-gpio",
120 .name
= "mikrotik,rb4xx-nand",
121 .of_compatible
= "mikrotik,rb4xx-nand",
125 static int rb4xx_cpld_probe(struct spi_device
*spi
)
127 struct device
*dev
= &spi
->dev
;
128 struct rb4xx_cpld
*cpld
;
131 cpld
= devm_kzalloc(dev
, sizeof(*cpld
), GFP_KERNEL
);
135 dev_set_drvdata(dev
, cpld
);
138 cpld
->write_nand
= rb4xx_cpld_write_nand
;
139 cpld
->read_nand
= rb4xx_cpld_read_nand
;
140 cpld
->gpio_set_0_7
= rb4xx_cpld_gpio_set_0_7
;
141 cpld
->gpio_set_8
= rb4xx_cpld_gpio_set_8
;
143 spi
->mode
= SPI_MODE_0
| SPI_TX_DUAL
;
144 ret
= spi_setup(spi
);
148 return devm_mfd_add_devices(dev
, PLATFORM_DEVID_NONE
,
150 ARRAY_SIZE(rb4xx_cpld_cells
),
154 static int rb4xx_cpld_remove(struct spi_device
*spi
)
159 static const struct of_device_id rb4xx_cpld_dt_match
[] = {
160 { .compatible
= "mikrotik,rb4xx-cpld", },
163 MODULE_DEVICE_TABLE(of
, rb4xx_cpld_dt_match
);
165 static struct spi_driver rb4xx_cpld_driver
= {
166 .probe
= rb4xx_cpld_probe
,
167 .remove
= rb4xx_cpld_remove
,
169 .name
= "rb4xx-cpld",
170 .bus
= &spi_bus_type
,
171 .of_match_table
= of_match_ptr(rb4xx_cpld_dt_match
),
175 module_spi_driver(rb4xx_cpld_driver
);
177 MODULE_DESCRIPTION("Mikrotik RB4xx CPLD driver");
178 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
179 MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
180 MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
181 MODULE_AUTHOR("Christopher Hill <ch6574@gmail.com");
182 MODULE_LICENSE("GPL v2");