diff --git a/klippy/extras/ad5206.py b/klippy/extras/ad5206.py
index a47644643..ec4db4e5b 100644
--- a/klippy/extras/ad5206.py
+++ b/klippy/extras/ad5206.py
@@ -8,9 +8,7 @@ class ad5206:
     def __init__(self, config):
         ppins = config.get_printer().lookup_object('pins')
         enable_pin = config.get('enable_pin')
-        enable_pin_params = ppins.lookup_pin('digital_out', enable_pin)
-        if enable_pin_params['invert']:
-            raise ppins.error("ad5206 can not invert pin")
+        enable_pin_params = ppins.lookup_pin(enable_pin)
         mcu = enable_pin_params['chip']
         pin = enable_pin_params['pin']
         scale = config.getfloat('scale', 1., above=0.)
diff --git a/klippy/extras/buttons.py b/klippy/extras/buttons.py
index 76727328b..8c5c2bd00 100644
--- a/klippy/extras/buttons.py
+++ b/klippy/extras/buttons.py
@@ -141,7 +141,7 @@ class PrinterButtons:
         mcu = mcu_name = None
         pin_params_list = []
         for pin in pins:
-            pin_params = ppins.lookup_pin('digital_in', pin)
+            pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True)
             if mcu is not None and pin_params['chip'] != mcu:
                 raise ppins.error("button pins must be on same mcu")
             mcu = pin_params['chip']
diff --git a/klippy/extras/display/hd44780.py b/klippy/extras/display/hd44780.py
index 6f1ebaab1..69fdcbb8b 100644
--- a/klippy/extras/display/hd44780.py
+++ b/klippy/extras/display/hd44780.py
@@ -23,15 +23,13 @@ class HD44780:
         self.printer = config.get_printer()
         # pin config
         ppins = self.printer.lookup_object('pins')
-        pins = [ppins.lookup_pin('digital_out', config.get(name + '_pin'))
+        pins = [ppins.lookup_pin(config.get(name + '_pin'))
                 for name in ['rs', 'e', 'd4', 'd5', 'd6', 'd7']]
         mcu = None
         for pin_params in pins:
             if mcu is not None and pin_params['chip'] != mcu:
                 raise ppins.error("hd44780 all pins must be on same mcu")
             mcu = pin_params['chip']
-            if pin_params['invert']:
-                raise ppins.error("hd44780 can not invert pin")
         self.pins = [pin_params['pin'] for pin_params in pins]
         self.mcu = mcu
         self.oid = self.mcu.create_oid()
diff --git a/klippy/extras/display/st7920.py b/klippy/extras/display/st7920.py
index eebc34288..006b23f06 100644
--- a/klippy/extras/display/st7920.py
+++ b/klippy/extras/display/st7920.py
@@ -17,15 +17,13 @@ class ST7920:
         printer = config.get_printer()
         # pin config
         ppins = printer.lookup_object('pins')
-        pins = [ppins.lookup_pin('digital_out', config.get(name + '_pin'))
+        pins = [ppins.lookup_pin(config.get(name + '_pin'))
                 for name in ['cs', 'sclk', 'sid']]
         mcu = None
         for pin_params in pins:
             if mcu is not None and pin_params['chip'] != mcu:
                 raise ppins.error("st7920 all pins must be on same mcu")
             mcu = pin_params['chip']
-            if pin_params['invert']:
-                raise ppins.error("st7920 can not invert pin")
         self.pins = [pin_params['pin'] for pin_params in pins]
         self.mcu = mcu
         self.oid = self.mcu.create_oid()
diff --git a/klippy/extras/display/uc1701.py b/klippy/extras/display/uc1701.py
index f76af0419..c2b865409 100644
--- a/klippy/extras/display/uc1701.py
+++ b/klippy/extras/display/uc1701.py
@@ -17,15 +17,13 @@ class UC1701:
         printer = config.get_printer()
         # pin config
         ppins = printer.lookup_object('pins')
-        pins = [ppins.lookup_pin('digital_out', config.get(name + '_pin'))
+        pins = [ppins.lookup_pin(config.get(name + '_pin'))
                 for name in ['cs','a0']]
         mcu = None
         for pin_params in pins:
             if mcu is not None and pin_params['chip'] != mcu:
                 raise ppins.error("uc1701 all pins must be on same mcu")
             mcu = pin_params['chip']
-            if pin_params['invert']:
-                raise ppins.error("uc1701 can not invert pin")
         self.pins = [pin_params['pin'] for pin_params in pins]
         self.mcu = mcu
         self.spi_oid = self.mcu.create_oid()
diff --git a/klippy/extras/multi_pin.py b/klippy/extras/multi_pin.py
index f378d4004..efa9676a8 100644
--- a/klippy/extras/multi_pin.py
+++ b/klippy/extras/multi_pin.py
@@ -15,21 +15,21 @@ class PrinterMultiPin:
         self.pin_type = None
         self.pin_list = [pin.strip() for pin in config.get('pins').split(',')]
         self.mcu_pins = []
-    def setup_pin(self, pin_params):
+    def setup_pin(self, pin_type, pin_params):
         ppins = self.printer.lookup_object('pins')
         pin_name = pin_params['pin']
         pin = self.printer.lookup_object('multi_pin ' + pin_name, None)
         if pin is not self:
             if pin is None:
                 raise ppins.error("multi_pin %s not configured" % (pin_name,))
-            return pin.setup_pin(pin_params)
+            return pin.setup_pin(pin_type, pin_params)
         if self.pin_type is not None:
             raise ppins.error("Can't setup multi_pin %s twice" % (pin_name,))
-        self.pin_type = pin_params['type']
+        self.pin_type = pin_type
         invert = ""
         if pin_params['invert']:
             invert = "!"
-        self.mcu_pins = [ppins.setup_pin(self.pin_type, invert + pin_desc)
+        self.mcu_pins = [ppins.setup_pin(pin_type, invert + pin_desc)
                          for pin_desc in self.pin_list]
         return self
     def get_mcu(self):
diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py
index 47ea7d73e..3c27e73b4 100644
--- a/klippy/extras/probe.py
+++ b/klippy/extras/probe.py
@@ -26,10 +26,11 @@ class PrinterProbe:
             self.z_position = pconfig.getfloat('minimum_z_position', 0.)
         # Create an "endstop" object to handle the probe pin
         ppins = self.printer.lookup_object('pins')
-        pin_params = ppins.lookup_pin('endstop', config.get('pin'))
+        pin = config.get('pin')
+        pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True)
         mcu = pin_params['chip']
         mcu.add_config_object(self)
-        self.mcu_probe = mcu.setup_pin(pin_params)
+        self.mcu_probe = mcu.setup_pin('endstop', pin_params)
         if (config.get('activate_gcode', None) is not None or
             config.get('deactivate_gcode', None) is not None):
             self.mcu_probe = ProbeEndstopWrapper(config, self.mcu_probe)
@@ -46,9 +47,8 @@ class PrinterProbe:
         kin = self.printer.lookup_object('toolhead').get_kinematics()
         for stepper in kin.get_steppers('Z'):
             stepper.add_to_endstop(self.mcu_probe)
-    def setup_pin(self, pin_params):
-        if (pin_params['pin'] != 'z_virtual_endstop'
-            or pin_params['type'] != 'endstop'):
+    def setup_pin(self, pin_type, pin_params):
+        if pin_type != 'endstop' or pin_params['pin'] != 'z_virtual_endstop':
             raise pins.error("Probe virtual endstop only useful as endstop pin")
         if pin_params['invert'] or pin_params['pullup']:
             raise pins.error("Can not pullup/invert probe virtual endstop")
diff --git a/klippy/extras/replicape.py b/klippy/extras/replicape.py
index 5bc851e99..91e755500 100644
--- a/klippy/extras/replicape.py
+++ b/klippy/extras/replicape.py
@@ -14,10 +14,10 @@ REPLICAPE_PCA9685_CYCLE_TIME = .001
 PIN_MIN_TIME = 0.100
 
 class pca9685_pwm:
-    def __init__(self, replicape, channel, pin_params):
+    def __init__(self, replicape, channel, pin_type, pin_params):
         self._replicape = replicape
         self._channel = channel
-        if pin_params['type'] not in ['digital_out', 'pwm']:
+        if pin_type not in ['digital_out', 'pwm']:
             raise pins.error("Pin type not supported on replicape")
         self._mcu = replicape.host_mcu
         self._mcu.add_config_object(self)
@@ -90,14 +90,14 @@ class pca9685_pwm:
             self.set_pwm(print_time, 0.)
 
 class ReplicapeDACEnable:
-    def __init__(self, replicape, channel, pin_params):
-        if pin_params['type'] != 'digital_out':
+    def __init__(self, replicape, channel, pin_type, pin_params):
+        if pin_type != 'digital_out':
             raise pins.error("Replicape virtual enable pin must be digital_out")
         if pin_params['invert']:
             raise pins.error("Replicape virtual enable pin can not be inverted")
         self.mcu = replicape.host_mcu
         self.value = replicape.stepper_dacs[channel]
-        self.pwm = pca9685_pwm(replicape, channel, pin_params)
+        self.pwm = pca9685_pwm(replicape, channel, pin_type, pin_params)
     def get_mcu(self):
         return self.mcu
     def setup_max_duration(self, max_duration):
@@ -221,12 +221,12 @@ class Replicape:
         clock = self.host_mcu.print_time_to_clock(print_time)
         # XXX - the spi_send message should be scheduled
         self.spi_send_cmd.send([self.sr_oid, sr], minclock=clock, reqclock=clock)
-    def setup_pin(self, pin_params):
+    def setup_pin(self, pin_type, pin_params):
         pin = pin_params['pin']
         if pin not in self.pins:
             raise pins.error("Unknown replicape pin %s" % (pin,))
         pclass, channel = self.pins[pin]
-        return pclass(self, channel, pin_params)
+        return pclass(self, channel, pin_type, pin_params)
 
 def load_config(config):
     return Replicape(config)
diff --git a/klippy/extras/spi_temperature.py b/klippy/extras/spi_temperature.py
index 10ea1a8fc..18e6dc690 100644
--- a/klippy/extras/spi_temperature.py
+++ b/klippy/extras/spi_temperature.py
@@ -26,8 +26,7 @@ class SensorBase:
         self.min_sample_value = self.max_sample_value = 0
         self._report_clock = 0
         ppins = config.get_printer().lookup_object('pins')
-        sensor_pin = config.get('sensor_pin')
-        pin_params = ppins.lookup_pin('digital_out', sensor_pin)
+        pin_params = ppins.lookup_pin(config.get('sensor_pin'))
         self.mcu = mcu = pin_params['chip']
         pin = pin_params['pin']
         # SPI bus configuration
diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py
index 001e62295..949145f29 100644
--- a/klippy/extras/tmc2130.py
+++ b/klippy/extras/tmc2130.py
@@ -26,9 +26,7 @@ class TMC2130:
         # pin setup
         ppins = self.printer.lookup_object("pins")
         cs_pin = config.get('cs_pin')
-        cs_pin_params = ppins.lookup_pin('digital_out', cs_pin)
-        if cs_pin_params['invert']:
-            raise pins.error("tmc2130 can not invert pin")
+        cs_pin_params = ppins.lookup_pin(cs_pin)
         self.mcu = cs_pin_params['chip']
         pin = cs_pin_params['pin']
         self.oid = self.mcu.create_oid()
@@ -113,9 +111,8 @@ class TMC2130:
         step_dist = stepper_config.getfloat('step_distance')
         step_dist_256 = step_dist / (1 << self.mres)
         return int(TMC_FREQUENCY * step_dist_256 / velocity + .5)
-    def setup_pin(self, pin_params):
-        if (pin_params['pin'] != 'virtual_endstop'
-            or pin_params['type'] != 'endstop'):
+    def setup_pin(self, pin_type, pin_params):
+        if pin_type != 'endstop' or pin_params['pin'] != 'virtual_endstop':
             raise pins.error("tmc2130 virtual endstop only useful as endstop")
         if pin_params['invert'] or pin_params['pullup']:
             raise pins.error("Can not pullup/invert tmc2130 virtual endstop")
diff --git a/klippy/mcu.py b/klippy/mcu.py
index a25fc3f73..3618e6a7c 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -610,10 +610,9 @@ class MCU:
         self.register_msg(self._handle_mcu_stats, 'stats')
         self._check_config()
     # Config creation helpers
-    def setup_pin(self, pin_params):
+    def setup_pin(self, pin_type, pin_params):
         pcs = {'stepper': MCU_stepper, 'endstop': MCU_endstop,
                'digital_out': MCU_digital_out, 'pwm': MCU_pwm, 'adc': MCU_adc}
-        pin_type = pin_params['type']
         if pin_type not in pcs:
             raise pins.error("pin type %s not supported on mcu" % (pin_type,))
         co = pcs[pin_type](self, pin_params)
diff --git a/klippy/pins.py b/klippy/pins.py
index 3d9d9ccff..20f782959 100644
--- a/klippy/pins.py
+++ b/klippy/pins.py
@@ -198,10 +198,8 @@ class PrinterPins:
     def __init__(self):
         self.chips = {}
         self.active_pins = {}
-    def lookup_pin(self, pin_type, pin_desc, share_type=None):
-        can_invert = pin_type in ['stepper', 'endstop', 'digital_in',
-                                  'digital_out', 'pwm']
-        can_pullup = pin_type in ['endstop', 'digital_in']
+    def lookup_pin(self, pin_desc, can_invert=False, can_pullup=False,
+                   share_type=None):
         desc = pin_desc.strip()
         pullup = invert = 0
         if can_pullup and desc.startswith('^'):
@@ -234,13 +232,15 @@ class PrinterPins:
                 raise error("Shared pin %s must have same polarity" % (pin,))
             return pin_params
         pin_params = {'chip': self.chips[chip_name], 'chip_name': chip_name,
-                      'type': pin_type, 'share_type': share_type,
-                      'pin': pin, 'invert': invert, 'pullup': pullup}
+                      'pin': pin, 'share_type': share_type,
+                      'invert': invert, 'pullup': pullup}
         self.active_pins[share_name] = pin_params
         return pin_params
     def setup_pin(self, pin_type, pin_desc):
-        pin_params = self.lookup_pin(pin_type, pin_desc)
-        return pin_params['chip'].setup_pin(pin_params)
+        can_invert = pin_type in ['stepper', 'endstop', 'digital_out', 'pwm']
+        can_pullup = pin_type in ['endstop']
+        pin_params = self.lookup_pin(pin_desc, can_invert, can_pullup)
+        return pin_params['chip'].setup_pin(pin_type, pin_params)
     def register_chip(self, chip_name, chip):
         chip_name = chip_name.strip()
         if chip_name in self.chips:
diff --git a/klippy/stepper.py b/klippy/stepper.py
index 8128a1309..073f33b6c 100644
--- a/klippy/stepper.py
+++ b/klippy/stepper.py
@@ -29,10 +29,11 @@ class StepperEnablePin:
 def lookup_enable_pin(ppins, pin):
     if pin is None:
         return StepperEnablePin(None, 9999)
-    pin_params = ppins.lookup_pin('digital_out', pin, 'stepper_enable')
+    pin_params = ppins.lookup_pin(pin, can_invert=True,
+                                  share_type='stepper_enable')
     enable = pin_params.get('class')
     if enable is None:
-        mcu_enable = pin_params['chip'].setup_pin(pin_params)
+        mcu_enable = pin_params['chip'].setup_pin('digital_out', pin_params)
         mcu_enable.setup_max_duration(0.)
         pin_params['class'] = enable = StepperEnablePin(mcu_enable)
     return enable
@@ -51,7 +52,8 @@ class PrinterStepper:
         # Stepper definition
         ppins = printer.lookup_object('pins')
         self.mcu_stepper = ppins.setup_pin('stepper', config.get('step_pin'))
-        dir_pin_params = ppins.lookup_pin('digital_out', config.get('dir_pin'))
+        dir_pin = config.get('dir_pin')
+        dir_pin_params = ppins.lookup_pin(dir_pin, can_invert=True)
         self.mcu_stepper.setup_dir_pin(dir_pin_params)
         step_dist = config.getfloat('step_distance', above=0.)
         self.mcu_stepper.setup_step_distance(step_dist)