From d3936d7437e388f3a91995e8f07fb82affff2f0d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 9 Jun 2017 20:33:10 +0200 Subject: gpio-exar/8250-exar: Fix passing in of parent PCI device This fixes reloading of the GPIO driver for the same platform device instance as created by the exar UART driver: First of all, the driver sets drvdata to its own value during probing and does not restore the original value on exit. But this won't help anyway as the core clears drvdata after the driver left. Set the platform device parent instead. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Acked-by: Greg Kroah-Hartman Signed-off-by: Linus Walleij --- drivers/tty/serial/8250/8250_exar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 1270ff163f63..2adc0f1a2196 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -196,7 +196,8 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) if (!pdev) return NULL; - platform_set_drvdata(pdev, pcidev); + pdev->dev.parent = &pcidev->dev; + if (platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; -- cgit v1.2.3-55-g7522 From a39f2fe7165647c2cd7bdbebb3d04061035e520f Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 25 May 2017 08:25:19 +0200 Subject: gpio-exar/8250-exar: Do not even instantiate a GPIO device for Commtech cards Commtech adapters need the MPIOs for internal purposes, and the gpio-exar driver already refused to pick them up. But there is actually no point in even creating the underlying platform device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Acked-by: Greg Kroah-Hartman --- drivers/gpio/gpio-exar.c | 3 --- drivers/tty/serial/8250/8250_exar.c | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 4b46273ca545..139d54008ad0 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -124,9 +124,6 @@ static int gpio_exar_probe(struct platform_device *pdev) void __iomem *p; int index, ret; - if (pcidev->vendor != PCI_VENDOR_ID_EXAR) - return -ENODEV; - /* * Map the pci device to get the register addresses. * We will need to read and write those registers to control diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 2adc0f1a2196..36877c8ad652 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -239,7 +239,9 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, /* Setup Multipurpose Input/Output pins. */ setup_gpio(p); - port->port.private_data = xr17v35x_register_gpio(pcidev); + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + xr17v35x_register_gpio(pcidev); } return 0; -- cgit v1.2.3-55-g7522 From 4076cf08ac7673aca7d4dd9ddf18045d08dbc292 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 21 May 2017 11:49:24 +0200 Subject: gpio-exar/8250-exar: Rearrange gpiochip parenthood Set the parent of the exar gpiochip to its platform device, like other gpiochips are doing it. In order to keep the relationship discoverable for ACPI systems, set the platform device companion to the PCI device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 2 +- drivers/tty/serial/8250/8250_exar.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index f3585a184f39..1a629831d45b 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -142,7 +142,7 @@ static int gpio_exar_probe(struct platform_device *pdev) sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; - exar_gpio->gpio_chip.parent = &pcidev->dev; + exar_gpio->gpio_chip.parent = &pdev->dev; exar_gpio->gpio_chip.direction_output = exar_direction_output; exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 36877c8ad652..32e3cb58193f 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -9,6 +9,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ +#include #include #include #include @@ -197,6 +198,7 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return NULL; pdev->dev.parent = &pcidev->dev; + ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); if (platform_device_add(pdev) < 0) { platform_device_put(pdev); -- cgit v1.2.3-55-g7522 From 0d963ebf57d4c6374b3a33050a18af23c1e2ede1 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 2 Jun 2017 07:27:59 +0200 Subject: serial: exar: Factor out platform hooks This prepares the addition of IOT2040 platform support by preparing the needed setup and rs485_config hooks. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 32e3cb58193f..575a252dc936 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -63,6 +63,11 @@ struct exar8250; +struct exar8250_platform { + int (*rs485_config)(struct uart_port *, struct serial_rs485 *); + int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); +}; + /** * struct exar8250_board - board information * @num_ports: number of serial ports @@ -189,7 +194,7 @@ static void setup_gpio(u8 __iomem *p) } static void * -xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev) { struct platform_device *pdev; @@ -208,17 +213,36 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static int xr17v35x_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + __xr17v35x_register_gpio(pcidev); + + return 0; +} + +static const struct exar8250_platform exar8250_default_platform = { + .register_gpio = xr17v35x_register_gpio, +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; + const struct exar8250_platform *platform; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; + platform = &exar8250_default_platform; + port->port.uartclk = baud * 16; + port->port.rs485_config = platform->rs485_config; + /* * Setup the uart clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz) @@ -241,12 +265,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, /* Setup Multipurpose Input/Output pins. */ setup_gpio(p); - if (pcidev->vendor == PCI_VENDOR_ID_EXAR) - port->port.private_data = - xr17v35x_register_gpio(pcidev); + ret = platform->register_gpio(pcidev, port); } - return 0; + return ret; } static void pci_xr17v35x_exit(struct pci_dev *pcidev) -- cgit v1.2.3-55-g7522 From 380b1e2f3a2f32bfe9c0aa85a68629eb99b043c0 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 22 May 2017 12:43:18 +0200 Subject: gpio-exar/8250-exar: Make set of exported GPIOs configurable On the SIMATIC, IOT2040 only a single pin is exportable as GPIO, the rest is required to operate the UART. To allow modeling this case, expand the platform device data structure to specify a (consecutive) pin subset for exporting by the gpio-exar driver. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 56 ++++++++++++++++++++++--------------- drivers/tty/serial/8250/8250_exar.c | 15 ++++++++-- 2 files changed, 45 insertions(+), 26 deletions(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 1a629831d45b..fb8d304cfa17 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -31,6 +31,7 @@ struct exar_gpio_chip { int index; void __iomem *regs; char name[20]; + unsigned int first_pin; }; static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, @@ -51,11 +52,12 @@ static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, static int exar_set_direction(struct gpio_chip *chip, int direction, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - exar_update(chip, addr, direction, offset % 8); + exar_update(chip, addr, direction, bit); return 0; } @@ -73,36 +75,33 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg) static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; - - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - val = exar_get(chip, addr) & BIT(offset % 8); + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static int exar_get_value(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; - - addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - val = exar_get(chip, addr) & BIT(offset % 8); + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static void exar_set_value(struct gpio_chip *chip, unsigned int offset, int value) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - exar_update(chip, addr, value, offset % 8); + exar_update(chip, addr, value, bit); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -121,6 +120,7 @@ static int gpio_exar_probe(struct platform_device *pdev) { struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent); struct exar_gpio_chip *exar_gpio; + u32 first_pin, ngpios; void __iomem *p; int index, ret; @@ -132,6 +132,15 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; + ret = device_property_read_u32(&pdev->dev, "linux,first-pin", + &first_pin); + if (ret) + return ret; + + ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios); + if (ret) + return ret; + exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL); if (!exar_gpio) return -ENOMEM; @@ -149,9 +158,10 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.get = exar_get_value; exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base = -1; - exar_gpio->gpio_chip.ngpio = 16; + exar_gpio->gpio_chip.ngpio = ngpios; exar_gpio->regs = p; exar_gpio->index = index; + exar_gpio->first_pin = first_pin; ret = devm_gpiochip_add_data(&pdev->dev, &exar_gpio->gpio_chip, exar_gpio); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 575a252dc936..9931e26ca061 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -194,7 +195,8 @@ static void setup_gpio(u8 __iomem *p) } static void * -__xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev, + const struct property_entry *properties) { struct platform_device *pdev; @@ -205,7 +207,8 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev) pdev->dev.parent = &pcidev->dev; ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); - if (platform_device_add(pdev) < 0) { + if (platform_device_add_properties(pdev, properties) < 0 || + platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; } @@ -213,12 +216,18 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static const struct property_entry exar_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 0), + PROPERTY_ENTRY_U32("ngpios", 16), + { } +}; + static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { if (pcidev->vendor == PCI_VENDOR_ID_EXAR) port->port.private_data = - __xr17v35x_register_gpio(pcidev); + __xr17v35x_register_gpio(pcidev, exar_gpio_properties); return 0; } -- cgit v1.2.3-55-g7522 From 413058df4331ce29f9934a5870d582c7e71fe15f Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 27 Jul 2016 16:32:20 +0200 Subject: serial: exar: Add support for IOT2040 device This implements the setup of RS232 and the switch-over to RS485 or RS422 for the Siemens IOT2040. That uses an EXAR XR17V352 with external logic to switch between the different modes. The external logic is controlled via MPIO pins of the EXAR controller. Only pin 10 can be exported as GPIO on the IOT2040. It is connected to an LED. As the XR17V352 used on the IOT2040 is not equipped with an external EEPROM, it cannot present itself as IOT2040-variant via subvendor/ subdevice IDs. Thus, we have to check via DMI for the target platform. Co-developed with Sascha Weisenberger. Signed-off-by: Sascha Weisenberger Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 129 +++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/8250/8250_exar.c') diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 9931e26ca061..b105f4cb51aa 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -10,6 +10,7 @@ * the Free Software Foundation; either version 2 of the License. */ #include +#include #include #include #include @@ -62,6 +63,43 @@ #define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ #define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ +#define UART_EXAR_RS485_DLY(x) ((x) << 4) + +/* + * IOT2040 MPIO wiring semantics: + * + * MPIO Port Function + * ---- ---- -------- + * 0 2 Mode bit 0 + * 1 2 Mode bit 1 + * 2 2 Terminate bus + * 3 - + * 4 3 Mode bit 0 + * 5 3 Mode bit 1 + * 6 3 Terminate bus + * 7 - + * 8 2 Enable + * 9 3 Enable + * 10 - Red LED + * 11..15 - + */ + +/* IOT2040 MPIOs 0..7 */ +#define IOT2040_UART_MODE_RS232 0x01 +#define IOT2040_UART_MODE_RS485 0x02 +#define IOT2040_UART_MODE_RS422 0x03 +#define IOT2040_UART_TERMINATE_BUS 0x04 + +#define IOT2040_UART1_MASK 0x0f +#define IOT2040_UART2_SHIFT 4 + +#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ +#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ + +/* IOT2040 MPIOs 8..15 */ +#define IOT2040_UARTS_ENABLE 0x03 +#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ + struct exar8250; struct exar8250_platform { @@ -236,18 +274,107 @@ static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, }; +static int iot2040_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 mask = IOT2040_UART1_MASK; + u8 mode, value; + + if (is_rs485) { + if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = IOT2040_UART_MODE_RS422; + else + mode = IOT2040_UART_MODE_RS485; + + if (rs485->flags & SER_RS485_TERMINATE_BUS) + mode |= IOT2040_UART_TERMINATE_BUS; + } else { + mode = IOT2040_UART_MODE_RS232; + } + + if (port->line == 3) { + mask <<= IOT2040_UART2_SHIFT; + mode <<= IOT2040_UART2_SHIFT; + } + + value = readb(p + UART_EXAR_MPIOLVL_7_0); + value &= ~mask; + value |= mode; + writeb(value, p + UART_EXAR_MPIOLVL_7_0); + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + port->rs485 = *rs485; + + return 0; +} + +static const struct property_entry iot2040_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 10), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static int iot2040_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + u8 __iomem *p = port->port.membase; + + writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); + writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); + writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); + writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); + + port->port.private_data = + __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + + return 0; +} + +static const struct exar8250_platform iot2040_platform = { + .rs485_config = iot2040_rs485_config, + .register_gpio = iot2040_register_gpio, +}; + +static const struct dmi_system_id exar_platforms[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-1YA2"), + }, + .driver_data = (void *)&iot2040_platform, + }, + {} +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; const struct exar8250_platform *platform; + const struct dmi_system_id *dmi_match; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; - platform = &exar8250_default_platform; + dmi_match = dmi_first_match(exar_platforms); + if (dmi_match) + platform = dmi_match->driver_data; + else + platform = &exar8250_default_platform; port->port.uartclk = baud * 16; port->port.rs485_config = platform->rs485_config; -- cgit v1.2.3-55-g7522