Felix Held | 3f3eca9 | 2020-01-23 17:12:32 +0100 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 2 | |
| 3 | /* Pre-RAM driver for SMSC LPC47N227 Super I/O chip. */ |
| 4 | |
Stefan Reinauer | 24d1d4b | 2013-03-21 11:51:41 -0700 | [diff] [blame] | 5 | #include <arch/io.h> |
Elyes HAOUAS | cd4fe0f | 2019-03-29 17:12:15 +0100 | [diff] [blame] | 6 | #include <assert.h> |
Kyösti Mälkki | 3855c01 | 2019-03-03 08:45:19 +0200 | [diff] [blame] | 7 | #include <device/pnp_ops.h> |
Elyes HAOUAS | cd4fe0f | 2019-03-29 17:12:15 +0100 | [diff] [blame] | 8 | |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 9 | #include "lpc47n227.h" |
| 10 | |
Antonello Dettori | 6321d7c | 2016-03-07 01:59:48 +0000 | [diff] [blame] | 11 | void pnp_enter_conf_state(pnp_devfn_t dev) |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 12 | { |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 13 | u16 port = dev >> 8; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 14 | outb(0x55, port); |
| 15 | } |
| 16 | |
Antonello Dettori | 6321d7c | 2016-03-07 01:59:48 +0000 | [diff] [blame] | 17 | void pnp_exit_conf_state(pnp_devfn_t dev) |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 18 | { |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 19 | u16 port = dev >> 8; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 20 | outb(0xaa, port); |
| 21 | } |
| 22 | |
Uwe Hermann | b69cb5a | 2010-10-26 22:46:43 +0000 | [diff] [blame] | 23 | /** |
| 24 | * Program the base I/O port for the specified logical device. |
| 25 | * |
| 26 | * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number. |
| 27 | * @param iobase Base I/O port for the logical device. |
| 28 | */ |
Edward O'Callaghan | 85836c2 | 2014-07-09 20:26:25 +1000 | [diff] [blame] | 29 | static void lpc47n227_pnp_set_iobase(pnp_devfn_t dev, u16 iobase) |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 30 | { |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 31 | /* LPC47N227 requires base ports to be a multiple of 4. */ |
Patrick Georgi | d635068 | 2012-05-18 12:47:02 +0200 | [diff] [blame] | 32 | /* it's not very useful to do an ASSERT here: if it trips, |
| 33 | * there's no console to report it. |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 34 | ASSERT(!(iobase & 0x3)); |
Patrick Georgi | d635068 | 2012-05-18 12:47:02 +0200 | [diff] [blame] | 35 | */ |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 36 | |
| 37 | switch (dev & 0xFF) { |
| 38 | case LPC47N227_PP: |
| 39 | pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff); |
| 40 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 41 | case LPC47N227_SP1: |
| 42 | pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff); |
| 43 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 44 | case LPC47N227_SP2: |
| 45 | pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff); |
| 46 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 47 | default: |
| 48 | break; |
| 49 | } |
| 50 | } |
| 51 | |
Uwe Hermann | b69cb5a | 2010-10-26 22:46:43 +0000 | [diff] [blame] | 52 | /** |
| 53 | * Enable or disable the specified logical device. |
| 54 | * |
| 55 | * Technically, a full disable requires setting the device's base I/O port |
| 56 | * below 0x100. We don't do that here, because we don't have access to a data |
| 57 | * structure that specifies what the 'real' base port is (when asked to enable |
| 58 | * the device). Also the function is used only to disable the device while its |
| 59 | * true base port is programmed (see lpc47n227_enable_serial() below). |
| 60 | * |
| 61 | * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number. |
Elyes HAOUAS | 36c6f956 | 2019-12-05 11:05:19 +0100 | [diff] [blame] | 62 | * @param enable 0 to disable, anything else to enable. |
Uwe Hermann | b69cb5a | 2010-10-26 22:46:43 +0000 | [diff] [blame] | 63 | */ |
Edward O'Callaghan | 85836c2 | 2014-07-09 20:26:25 +1000 | [diff] [blame] | 64 | static void lpc47n227_pnp_set_enable(pnp_devfn_t dev, int enable) |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 65 | { |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 66 | u8 power_register = 0, power_mask = 0, current_power, new_power; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 67 | |
| 68 | switch (dev & 0xFF) { |
| 69 | case LPC47N227_PP: |
| 70 | power_register = 0x01; |
| 71 | power_mask = 0x04; |
| 72 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 73 | case LPC47N227_SP1: |
| 74 | power_register = 0x02; |
| 75 | power_mask = 0x08; |
| 76 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 77 | case LPC47N227_SP2: |
| 78 | power_register = 0x02; |
| 79 | power_mask = 0x80; |
| 80 | break; |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 81 | default: |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | current_power = pnp_read_config(dev, power_register); |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 86 | new_power = current_power & ~power_mask; /* Disable by default. */ |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 87 | if (enable) |
Uwe Hermann | a69d978 | 2010-11-15 19:35:14 +0000 | [diff] [blame] | 88 | new_power |= power_mask; /* Enable. */ |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 89 | pnp_write_config(dev, power_register, new_power); |
| 90 | } |
| 91 | |
Uwe Hermann | b69cb5a | 2010-10-26 22:46:43 +0000 | [diff] [blame] | 92 | /** |
| 93 | * Configure the base I/O port of the specified serial device and enable the |
| 94 | * serial device. |
| 95 | * |
| 96 | * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number. |
| 97 | * @param iobase Processor I/O port address to assign to this serial device. |
| 98 | */ |
Antonello Dettori | 6321d7c | 2016-03-07 01:59:48 +0000 | [diff] [blame] | 99 | void lpc47n227_enable_serial(pnp_devfn_t dev, u16 iobase) |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 100 | { |
Uwe Hermann | b69cb5a | 2010-10-26 22:46:43 +0000 | [diff] [blame] | 101 | /* |
| 102 | * NOTE: Cannot use pnp_set_XXX() here because they assume chip |
| 103 | * support for logical devices, which the LPC47N227 doesn't have. |
| 104 | */ |
Stefan Reinauer | 55426df | 2010-01-17 13:50:17 +0000 | [diff] [blame] | 105 | pnp_enter_conf_state(dev); |
| 106 | lpc47n227_pnp_set_enable(dev, 0); |
| 107 | lpc47n227_pnp_set_iobase(dev, iobase); |
| 108 | lpc47n227_pnp_set_enable(dev, 1); |
| 109 | pnp_exit_conf_state(dev); |
| 110 | } |