/* * SCU board driver * * Copyright (c) 2012, 2014 Guenter Roeck * * 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; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ... static const char *pca9538_ext0_gpio_names[8] = { "pca9538_ext0:wireless_ena_1", "pca9538_ext0:wireless_ena_2", "pca9538_ext0:wireless_a_radio_disable", "pca9538_ext0:wireless_a_reset", "pca9538_ext0:in_spare_1", "pca9538_ext0:in_spare_2", "pca9538_ext0:wireless_b_radio_disable", "pca9538_ext0:wireless_b_reset", }; static const char *pca9538_ext1_gpio_names[8] = { "pca9538_ext1:rd_led_on", "pca9538_ext1:wless_led_on", "pca9538_ext1:ld_fail_led_on", "pca9538_ext1:sw_led_on", "pca9538_ext1:discrete_out_1", "pca9538_ext1:discrete_out_2", "pca9538_ext1:discrete_out_3", "pca9538_ext1:discrete_out_4", }; static const char *pca9538_ext2_gpio_names[8] = { "pca9538_ext2:sd_active_1", "pca9538_ext2:sd_error_1", "pca9538_ext2:sd_active_2", "pca9538_ext2:sd_error_2", "pca9538_ext2:sd_active_3", "pca9538_ext2:sd_error_3", "pca9538_ext2:hub_6_reset", "pca9538_ext2:hub_6_config_status", }; static const char *pca9538_ext3_gpio_names[8] = { "pca9538_ext3:sd_active_4", "pca9538_ext3:sd_error_4", "pca9538_ext3:sd_active_5", "pca9538_ext3:sd_error_5", "pca9538_ext3:sd_active_6", "pca9538_ext3:sd_error_6", "pca9538_ext3:hub_2_reset", "pca9538_ext3:hub_2_config_status", }; static const char *pca9557_gpio_names[8] = { "pca9557:sd_card_detect_1", "pca9557:sd_card_detect_2", "pca9557:sd_card_detect_3", "pca9557:sd_card_detect_4", "pca9557:sd_card_detect_5", "pca9557:sd_card_detect_6", "pca9557:spare1", "pca9557:spare2", }; static int scu_gpio_common_setup(unsigned gpio_base, unsigned ngpio, u32 mask, u32 is_input, u32 is_active, u32 active_low) { int i; unsigned long flags; for (i = 0; i < ngpio; i++) { if (!(mask & (1 << i))) continue; flags = GPIOF_EXPORT_DIR_FIXED; if (is_input & (1 << i)) { flags |= GPIOF_DIR_IN; } else { flags |= GPIOF_DIR_OUT; if (is_active & (1 << i)) flags |= GPIOF_INIT_HIGH; } if (active_low & (1 << i)) flags |= GPIOF_ACTIVE_LOW; gpio_request_one(gpio_base + i, flags, NULL); } return 0; } static int pca9538_ext0_setup(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_setup(gpio_base, ngpio, 0xff, 0x33, 0xcc, 0x00); return 0; } static int pca9538_ext1_setup(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_setup(gpio_base, ngpio, 0xf0, 0x00, 0x00, 0x00); return 0; } static int pca9538_ext2_setup(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_setup(gpio_base, ngpio, 0xc0, 0x80, 0x40, 0x00); return 0; } static int pca9538_ext3_setup(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_setup(gpio_base, ngpio, 0xc0, 0x80, 0x40, 0x00); return 0; } static int pca9557_setup(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_setup(gpio_base, ngpio, 0x3f, 0x3f, 0x00, 0x3f); return 0; } static void scu_gpio_common_teardown(unsigned gpio_base, int ngpio, u32 mask) { int i; for (i = 0; i < ngpio; i++) { if (mask & (1 << i)) { gpio_unexport(gpio_base + i); gpio_free(gpio_base + i); } } } static int pca9538_ext0_teardown(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_teardown(gpio_base, ngpio, 0xff); return 0; } static int pca9538_ext1_teardown(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_teardown(gpio_base, ngpio, 0xf0); return 0; } static int pca9538_ext2_teardown(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_teardown(gpio_base, ngpio, 0xc0); return 0; } static int pca9538_ext3_teardown(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_teardown(gpio_base, ngpio, 0xc0); return 0; } static int pca9557_teardown(struct i2c_client *client, unsigned gpio_base, unsigned ngpio, void *context) { scu_gpio_common_teardown(gpio_base, ngpio, 0x3f); return 0; } static struct pca953x_platform_data scu_pca953x_pdata[] = { [0] = {.gpio_base = SCU_EXT_GPIO_BASE(0), .irq_base = -1, .setup = pca9538_ext0_setup, .teardown = pca9538_ext0_teardown, .names = pca9538_ext0_gpio_names}, [1] = {.gpio_base = SCU_EXT_GPIO_BASE(1), .irq_base = -1, .setup = pca9538_ext1_setup, .teardown = pca9538_ext1_teardown, .names = pca9538_ext1_gpio_names}, [2] = {.gpio_base = SCU_EXT_GPIO_BASE(2), .irq_base = -1, .setup = pca9538_ext2_setup, .teardown = pca9538_ext2_teardown, .names = pca9538_ext2_gpio_names}, [3] = {.gpio_base = SCU_EXT_GPIO_BASE(3), .irq_base = -1, .setup = pca9538_ext3_setup, .teardown = pca9538_ext3_teardown, .names = pca9538_ext3_gpio_names}, [4] = {.gpio_base = SCU_EXT_GPIO_BASE(4), .irq_base = -1, .setup = pca9557_setup, .teardown = pca9557_teardown, .names = pca9557_gpio_names}, }; ... static int scu_instantiate_i2c(struct scu_data *data, int base, struct i2c_board_info *info, int count) { int i; for (i = 0; i < count; i++) { data->client[base + i] = i2c_new_device(data->adapter, info); if (!data->client[base + i]) { /* * Unfortunately this call does not tell us * why it failed. Pick the most likely reason. */ return -EBUSY; } info++; } return 0; } static struct i2c_board_info scu_i2c_info_common[] = { { I2C_BOARD_INFO("scu_pic", 0x20)}, { I2C_BOARD_INFO("at24", 0x54), .platform_data = &at24c08}, { I2C_BOARD_INFO("ds1682", 0x6b)}, { I2C_BOARD_INFO("pca9538", 0x71), .platform_data = &scu_pca953x_pdata[1],}, { I2C_BOARD_INFO("pca9538", 0x72), .platform_data = &scu_pca953x_pdata[2],}, { I2C_BOARD_INFO("pca9538", 0x73), .platform_data = &scu_pca953x_pdata[3],}, }; ... static int scu_probe(struct platform_device *pdev) { struct proc_dir_entry *rave_board_type; struct device *dev = &pdev->dev; struct i2c_adapter *adapter; struct net_device *ndev; struct scu_data *data; int i, ret; ... ret = scu_instantiate_i2c(data, 0, scu_i2c_info_common, ARRAY_SIZE(scu_i2c_info_common)); ... }