1 /* 2 * I2C multiplexer using GPIO API 3 * 4 * Peter Korsgaard <peter.korsgaard@barco.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11 #include <linux/i2c.h> 12 #include <linux/i2c-mux.h> 13 #include <linux/i2c-mux-gpio.h> 14 #include <linux/platform_device.h> 15 #include <linux/init.h> 16 #include <linux/module.h> 17 #include <linux/slab.h> 18 #include <linux/gpio.h> 19 20 struct gpiomux { 21 struct i2c_adapter *parent; 22 struct i2c_adapter **adap; /* child busses */ 23 struct i2c_mux_gpio_platform_data data; 24 }; 25 26 static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) 27 { 28 int i; 29 30 for (i = 0; i < mux->data.n_gpios; i++) 31 gpio_set_value(mux->data.gpios[i], val & (1 << i)); 32 } 33 34 static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan) 35 { 36 struct gpiomux *mux = data; 37 38 i2c_mux_gpio_set(mux, mux->data.values[chan]); 39 40 return 0; 41 } 42 43 static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan) 44 { 45 struct gpiomux *mux = data; 46 47 i2c_mux_gpio_set(mux, mux->data.idle); 48 49 return 0; 50 } 51 52 static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) 53 { 54 struct gpiomux *mux; 55 struct i2c_mux_gpio_platform_data *pdata; 56 struct i2c_adapter *parent; 57 int (*deselect) (struct i2c_adapter *, void *, u32); 58 unsigned initial_state; 59 int i, ret; 60 61 pdata = pdev->dev.platform_data; 62 if (!pdata) { 63 dev_err(&pdev->dev, "Missing platform data\n"); 64 return -ENODEV; 65 } 66 67 parent = i2c_get_adapter(pdata->parent); 68 if (!parent) { 69 dev_err(&pdev->dev, "Parent adapter (%d) not found\n", 70 pdata->parent); 71 return -ENODEV; 72 } 73 74 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 75 if (!mux) { 76 ret = -ENOMEM; 77 goto alloc_failed; 78 } 79 80 mux->parent = parent; 81 mux->data = *pdata; 82 mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, 83 GFP_KERNEL); 84 if (!mux->adap) { 85 ret = -ENOMEM; 86 goto alloc_failed2; 87 } 88 89 if (pdata->idle != I2C_MUX_GPIO_NO_IDLE) { 90 initial_state = pdata->idle; 91 deselect = i2c_mux_gpio_deselect; 92 } else { 93 initial_state = pdata->values[0]; 94 deselect = NULL; 95 } 96 97 for (i = 0; i < pdata->n_gpios; i++) { 98 ret = gpio_request(pdata->gpios[i], "i2c-mux-gpio"); 99 if (ret) 100 goto err_request_gpio; 101 gpio_direction_output(pdata->gpios[i], 102 initial_state & (1 << i)); 103 } 104 105 for (i = 0; i < pdata->n_values; i++) { 106 u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; 107 108 mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, i, 109 i2c_mux_gpio_select, deselect); 110 if (!mux->adap[i]) { 111 ret = -ENODEV; 112 dev_err(&pdev->dev, "Failed to add adapter %d\n", i); 113 goto add_adapter_failed; 114 } 115 } 116 117 dev_info(&pdev->dev, "%d port mux on %s adapter\n", 118 pdata->n_values, parent->name); 119 120 platform_set_drvdata(pdev, mux); 121 122 return 0; 123 124 add_adapter_failed: 125 for (; i > 0; i--) 126 i2c_del_mux_adapter(mux->adap[i - 1]); 127 i = pdata->n_gpios; 128 err_request_gpio: 129 for (; i > 0; i--) 130 gpio_free(pdata->gpios[i - 1]); 131 kfree(mux->adap); 132 alloc_failed2: 133 kfree(mux); 134 alloc_failed: 135 i2c_put_adapter(parent); 136 137 return ret; 138 } 139 140 static int __devexit i2c_mux_gpio_remove(struct platform_device *pdev) 141 { 142 struct gpiomux *mux = platform_get_drvdata(pdev); 143 int i; 144 145 for (i = 0; i < mux->data.n_values; i++) 146 i2c_del_mux_adapter(mux->adap[i]); 147 148 for (i = 0; i < mux->data.n_gpios; i++) 149 gpio_free(mux->data.gpios[i]); 150 151 platform_set_drvdata(pdev, NULL); 152 i2c_put_adapter(mux->parent); 153 kfree(mux->adap); 154 kfree(mux); 155 156 return 0; 157 } 158 159 static struct platform_driver i2c_mux_gpio_driver = { 160 .probe = i2c_mux_gpio_probe, 161 .remove = __devexit_p(i2c_mux_gpio_remove), 162 .driver = { 163 .owner = THIS_MODULE, 164 .name = "i2c-mux-gpio", 165 }, 166 }; 167 168 module_platform_driver(i2c_mux_gpio_driver); 169 170 MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); 171 MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>"); 172 MODULE_LICENSE("GPL"); 173 MODULE_ALIAS("platform:i2c-mux-gpio"); 174