blob: 795288fe2e94553760d0e8f0c93a997cba94a8cd [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Michal Simekd79ac322016-04-25 10:50:42 +02002/*
3 * Copyright (C) 2015 - 2016 Xilinx, Inc.
Moritz Fischer36bdeb72017-09-12 06:46:59 -07004 * Copyright (C) 2017 National Instruments Corp
Michal Simekd79ac322016-04-25 10:50:42 +02005 * Written by Michal Simek
Michal Simekd79ac322016-04-25 10:50:42 +02006 */
7
Michal Simekd79ac322016-04-25 10:50:42 +02008#include <dm.h>
9#include <errno.h>
10#include <i2c.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.h>
Simon Glass336d4612020-02-03 07:36:16 -070012#include <malloc.h>
Moritz Fischer36bdeb72017-09-12 06:46:59 -070013
14#include <asm-generic/gpio.h>
Michal Simekd79ac322016-04-25 10:50:42 +020015
Marek Behún8e6eda72017-06-09 19:28:43 +020016enum pca_type {
Luca Ceresoli9985b742019-04-09 08:57:43 +020017 PCA9543,
Marek Behún8e6eda72017-06-09 19:28:43 +020018 PCA9544,
Chris Packham0b1d7b72020-04-01 15:55:27 +130019 PCA9546,
Marek Behún8e6eda72017-06-09 19:28:43 +020020 PCA9547,
Peng Fan16f513e2018-07-17 20:38:32 +080021 PCA9548,
Vladimir Oltean3285a572022-01-03 14:47:22 +020022 PCA9646,
23 PCA9847,
Marek Behún8e6eda72017-06-09 19:28:43 +020024};
25
26struct chip_desc {
Luca Ceresoli5995cdb2019-04-09 08:57:42 +020027 u8 enable; /* Enable mask in ctl register (used for muxes only) */
Marek Behún8e6eda72017-06-09 19:28:43 +020028 enum muxtype {
29 pca954x_ismux = 0,
30 pca954x_isswi,
31 } muxtype;
Chris Packham5bc90a82017-09-29 10:53:36 +130032 u32 width;
Marek Behún8e6eda72017-06-09 19:28:43 +020033};
34
Michal Simekd79ac322016-04-25 10:50:42 +020035struct pca954x_priv {
36 u32 addr; /* I2C mux address */
37 u32 width; /* I2C mux width - number of busses */
Moritz Fischer36bdeb72017-09-12 06:46:59 -070038 struct gpio_desc gpio_mux_reset;
Michal Simekd79ac322016-04-25 10:50:42 +020039};
40
Marek Behún8e6eda72017-06-09 19:28:43 +020041static const struct chip_desc chips[] = {
Luca Ceresoli9985b742019-04-09 08:57:43 +020042 [PCA9543] = {
43 .muxtype = pca954x_isswi,
44 .width = 2,
45 },
Marek Behún8e6eda72017-06-09 19:28:43 +020046 [PCA9544] = {
47 .enable = 0x4,
48 .muxtype = pca954x_ismux,
Chris Packham5bc90a82017-09-29 10:53:36 +130049 .width = 4,
Marek Behún8e6eda72017-06-09 19:28:43 +020050 },
Chris Packham0b1d7b72020-04-01 15:55:27 +130051 [PCA9546] = {
52 .muxtype = pca954x_isswi,
53 .width = 4,
54 },
Marek Behún8e6eda72017-06-09 19:28:43 +020055 [PCA9547] = {
56 .enable = 0x8,
57 .muxtype = pca954x_ismux,
Chris Packham5bc90a82017-09-29 10:53:36 +130058 .width = 8,
Marek Behún8e6eda72017-06-09 19:28:43 +020059 },
60 [PCA9548] = {
Marek Behún8e6eda72017-06-09 19:28:43 +020061 .muxtype = pca954x_isswi,
Chris Packham5bc90a82017-09-29 10:53:36 +130062 .width = 8,
Marek Behún8e6eda72017-06-09 19:28:43 +020063 },
Peng Fan16f513e2018-07-17 20:38:32 +080064 [PCA9646] = {
Peng Fan16f513e2018-07-17 20:38:32 +080065 .muxtype = pca954x_isswi,
66 .width = 4,
67 },
Vladimir Oltean3285a572022-01-03 14:47:22 +020068 [PCA9847] = {
69 .enable = 0x8,
70 .muxtype = pca954x_ismux,
71 .width = 8,
72 },
Marek Behún8e6eda72017-06-09 19:28:43 +020073};
74
Michal Simekd79ac322016-04-25 10:50:42 +020075static int pca954x_deselect(struct udevice *mux, struct udevice *bus,
76 uint channel)
77{
78 struct pca954x_priv *priv = dev_get_priv(mux);
79 uchar byte = 0;
80
81 return dm_i2c_write(mux, priv->addr, &byte, 1);
82}
83
84static int pca954x_select(struct udevice *mux, struct udevice *bus,
85 uint channel)
86{
87 struct pca954x_priv *priv = dev_get_priv(mux);
Marek Behún8e6eda72017-06-09 19:28:43 +020088 const struct chip_desc *chip = &chips[dev_get_driver_data(mux)];
89 uchar byte;
90
91 if (chip->muxtype == pca954x_ismux)
92 byte = channel | chip->enable;
93 else
94 byte = 1 << channel;
Michal Simekd79ac322016-04-25 10:50:42 +020095
96 return dm_i2c_write(mux, priv->addr, &byte, 1);
97}
98
99static const struct i2c_mux_ops pca954x_ops = {
100 .select = pca954x_select,
101 .deselect = pca954x_deselect,
102};
103
104static const struct udevice_id pca954x_ids[] = {
Luca Ceresoli9985b742019-04-09 08:57:43 +0200105 { .compatible = "nxp,pca9543", .data = PCA9543 },
Marek Behún8e6eda72017-06-09 19:28:43 +0200106 { .compatible = "nxp,pca9544", .data = PCA9544 },
Chris Packham0b1d7b72020-04-01 15:55:27 +1300107 { .compatible = "nxp,pca9546", .data = PCA9546 },
Marek Behún8e6eda72017-06-09 19:28:43 +0200108 { .compatible = "nxp,pca9547", .data = PCA9547 },
109 { .compatible = "nxp,pca9548", .data = PCA9548 },
Peng Fan16f513e2018-07-17 20:38:32 +0800110 { .compatible = "nxp,pca9646", .data = PCA9646 },
Vladimir Oltean3285a572022-01-03 14:47:22 +0200111 { .compatible = "nxp,pca9847", .data = PCA9847 },
Michal Simekd79ac322016-04-25 10:50:42 +0200112 { }
113};
114
Simon Glassd1998a92020-12-03 16:55:21 -0700115static int pca954x_of_to_plat(struct udevice *dev)
Michal Simekd79ac322016-04-25 10:50:42 +0200116{
117 struct pca954x_priv *priv = dev_get_priv(dev);
Chris Packham5bc90a82017-09-29 10:53:36 +1300118 const struct chip_desc *chip = &chips[dev_get_driver_data(dev)];
Michal Simekd79ac322016-04-25 10:50:42 +0200119
Michal Simek58dc4a92019-01-09 11:58:24 +0100120 priv->addr = dev_read_u32_default(dev, "reg", 0);
Michal Simekd79ac322016-04-25 10:50:42 +0200121 if (!priv->addr) {
122 debug("MUX not found\n");
123 return -ENODEV;
124 }
Chris Packham5bc90a82017-09-29 10:53:36 +1300125 priv->width = chip->width;
Michal Simekd79ac322016-04-25 10:50:42 +0200126
127 if (!priv->width) {
128 debug("No I2C MUX width specified\n");
129 return -EINVAL;
130 }
131
132 debug("Device %s at 0x%x with width %d\n",
133 dev->name, priv->addr, priv->width);
134
135 return 0;
136}
137
Moritz Fischer36bdeb72017-09-12 06:46:59 -0700138static int pca954x_probe(struct udevice *dev)
139{
Simon Glassbcee8d62019-12-06 21:41:35 -0700140 if (CONFIG_IS_ENABLED(DM_GPIO)) {
Moritz Fischer36bdeb72017-09-12 06:46:59 -0700141 struct pca954x_priv *priv = dev_get_priv(dev);
142 int err;
143
144 err = gpio_request_by_name(dev, "reset-gpios", 0,
145 &priv->gpio_mux_reset, GPIOD_IS_OUT);
146
147 /* it's optional so only bail if we get a real error */
148 if (err && (err != -ENOENT))
149 return err;
150
151 /* dm will take care of polarity */
152 if (dm_gpio_is_valid(&priv->gpio_mux_reset))
153 dm_gpio_set_value(&priv->gpio_mux_reset, 0);
154 }
155
156 return 0;
157}
158
159static int pca954x_remove(struct udevice *dev)
160{
Simon Glassbcee8d62019-12-06 21:41:35 -0700161 if (CONFIG_IS_ENABLED(DM_GPIO)) {
Moritz Fischer36bdeb72017-09-12 06:46:59 -0700162 struct pca954x_priv *priv = dev_get_priv(dev);
163
164 if (dm_gpio_is_valid(&priv->gpio_mux_reset))
165 dm_gpio_free(dev, &priv->gpio_mux_reset);
166 }
167
168 return 0;
169}
170
Michal Simekd79ac322016-04-25 10:50:42 +0200171U_BOOT_DRIVER(pca954x) = {
172 .name = "pca954x",
173 .id = UCLASS_I2C_MUX,
174 .of_match = pca954x_ids,
Moritz Fischer36bdeb72017-09-12 06:46:59 -0700175 .probe = pca954x_probe,
176 .remove = pca954x_remove,
Michal Simekd79ac322016-04-25 10:50:42 +0200177 .ops = &pca954x_ops,
Simon Glassd1998a92020-12-03 16:55:21 -0700178 .of_to_plat = pca954x_of_to_plat,
Simon Glass41575d82020-12-03 16:55:17 -0700179 .priv_auto = sizeof(struct pca954x_priv),
Michal Simekd79ac322016-04-25 10:50:42 +0200180};