Macros can be made to work with more arguments if you wish. They are rarely a good solution, however. The question has been asked before but I will repeat it. Why do you need the code to work this way? It is clear (and certainly reasonable!) that you want to get away from using a massive switch statement to handle this. What isn't clear, however, is why that would be a problem. When at all possible, I prefer to avoid macros because they happen only on text before the compiler sees the program. That can lead to annoying bugs and inconsistency in how the code works. Still, they would work here -- either as ,
input_w_pullup(PA5);
or as something like,
input_w_pullup(PORTA, 5);
Actually, these could be easily implemented as static inline functions and have the same cost as a macro.
I usually code in C++ and handle the same thing by wrapping the port/pin in a class. That way I can pass them around as one piece of data. This works just fine in pure C too (as it may look for an AVR). It takes a bit of work to get the compiler to see that it can remove all the abstraction cost though. So just immediate functions might be preferable.
I would look very closely at the comments by IanB and ajb. They are pointing you in a good direction as they are showing how you can make a function handle multiple situations without a massive switch/case statement. Even if it takes some math to calculate offsets or something, trust the compiler. They are exceptionally good these days. You'd be surprised what it can figure out it doesn't need.
Cheers,
Martin Jay McKee