Hi Herbert,

On Wed, 1 Oct 2008 19:08:27 +0200, Herbert Poetzl wrote:
> 
> here is the latest version of the I2C floppy 
> controller bus driver, which is now completely
> configurable and can handle 'single floppy' type
> controllers (where the second motor control is
> missing).
> 
> please consider for (mainline) inclusion!
> 
> TIA,
> Herbert
> 
> Signed-off-by: Herbert Poetzl <[EMAIL PROTECTED]>
> 
> diff -NurpP --minimal linux-2.6.27-rc8/Documentation/i2c/busses/i2c-floppy 
> linux-2.6.27-rc8-fi2c-v0.6.1/Documentation/i2c/busses/i2c-floppy
> --- linux-2.6.27-rc8/Documentation/i2c/busses/i2c-floppy      1970-01-01 
> 01:00:00.000000000 +0100
> +++ linux-2.6.27-rc8-fi2c-v0.6.1/Documentation/i2c/busses/i2c-floppy  
> 2008-09-30 22:56:14.000000000 +0200
> @@ -0,0 +1,151 @@
> +Kernel driver i2c-floppy
> +
> +Author: Herbert Poetzl <[EMAIL PROTECTED]>
> +
> +This driver is for a simple do-it-yourself floppy controller
> +to I2C adapter which uses direct I/O access and the sense
> +command to control the output lines and query the input lines.
> +
> +Potential output lines are (for now):
> +
> + Motor Enable A  [/MOTEA,  10]
> + Drive Select B  [/DRVSB,  12]
> + Drive Select A  [/DRVSA,  14]
> + Motor Enable B  [/MOTEB,  16]
> + Head Select     [/SIDE1,  32]
> +
> +Potential input lines are (for now):
> +
> + Track 0         [/TRK00,  26]
> + Write Protect   [/WPT,    28]
> + Disk Change     [/DSKCHG, 34]
> +
> +Not all possible combinations are useable, as for example
> +the Drive Select lines depend on the respective Motor Enable
> +line. Nevertheless, the possible combinations can be selected
> +from the following table:
> +
> + Mode             ENA             SDA          SCL

I don't understand what "ENA" is supposed to represent.

> +-------------------------------------------------------
> +   1               -          /MOTEA [10]  /MOTEB [16]
> +   2          /SIDE1 [32]     /MOTEA [10]  /MOTEB [16]
> +  -----------------------------------------------------
> +   3               -          /MOTEA [10]  /DRVSB [12]
> +   4          /MOTEB [16]     /MOTEA [10]  /DRVSB [12]
> +   5          /SIDE1 [32]     /MOTEA [10]  /DRVSB [12]
> +  -----------------------------------------------------
> +   6               -          /MOTEA [10]  /SIDE1 [32]
> +   7          /MOTEB [16]     /MOTEA [10]  /SIDE1 [32]
> +   8          /DRVSB [12]     /MOTEA [10]  /SIDE1 [32]
> +  -----------------------------------------------------
> +   9               -          /MOTEB [16]  /DRVSA [14]
> +  10 [0a]     /MOTEA [10]     /MOTEB [16]  /DRVSA [14]
> +  11 [0b]     /SIDE1 [32]     /MOTEB [16]  /DRVSA [14]
> +  -----------------------------------------------------
> +  12 [0c]          -          /MOTEB [16]  /SIDE1 [32]
> +  13 [0d]     /MOTEA [10]     /MOTEB [16]  /SIDE1 [32]
> +  14 [0e]     /DRVSA [14]     /MOTEB [16]  /SIDE1 [32]
> +  -----------------------------------------------------
> +  15 [0f]          -          /SIDE1 [32]  /DRVSA [14]
> +  16 [10]     /MOTEA [10]     /SIDE1 [32]  /DRVSA [14]
> +  17 [11]     /MOTEB [16]     /SIDE1 [32]  /DRVSA [14]
> +  18 [12]     /DRVSB [12]     /SIDE1 [32]  /DRVSA [14]
> +  -----------------------------------------------------
> +  19 [13]          -          /SIDE1 [32]  /DRVSB [12]
> +  20 [14]     /MOTEA [10]     /SIDE1 [32]  /DRVSB [12]
> +  21 [15]     /MOTEB [16]     /SIDE1 [32]  /DRVSB [12]
> +  22 [16]     /DRVSA [14]     /SIDE1 [32]  /DRVSB [12]

What's the value of numbering modes both in decimal and hexadecimal? I
think it adds to confusion more than it helps. You should pick one and
stick to it.

> +-------------------------------------------------------
> +
> +The following modifiers can be added to the selection:
> +
> +  32 0x0020      Swap SCL and SDA
> +  64 0x0040      Invert SDA Output
> + 128 0x0080      Invert SCL Output
> + 256 0x0100      Invert ENA Output
> +
> +
> +The input can be selected in a similar way, although it is
> +probably easier to use the autodetect feature there.
> +Nevertheless here is the table:
> +
> + Mode            ENA            SDA           SCL
> +-------------------------------------------------------
> +   0         auto detect    auto detect   auto detect
> +  -----------------------------------------------------
> +   1              -         /DSKCHG [34]       -
> +   2              -         /TRK00  [26]       -
> +   3              -         /WPT    [28]       -
> +  -----------------------------------------------------
> +   4              -         /DSKCHG [34]  /TRK00  [26]
> +   5              -         /TRK00  [26]  /WPT    [28]
> +   6              -         /WPT    [28]  /DSKCHG [34]
> +  -----------------------------------------------------
> +   7         /DSKCHG [34]   /TRK00  [26]       -
> +   8         /DSKCHG [34]   /WPT    [28]       -
> +   9         /DSKCHG [34]   /TRK00  [26]  /WPT    [28]
> +  -----------------------------------------------------
> +  10 [0a]    /TRK00  [26]   /DSKCHG [34]       -
> +  11 [0b]    /TRK00  [26]   /WPT    [28]       -
> +  12 [0c]    /TRK00  [26]   /WPT    [28]  /DSKCHG [34]
> +  -----------------------------------------------------
> +  13 [0d]    /WPT    [28]   /DSKCHG [34]       -
> +  14 [0e]    /WPT    [28]   /TRK00  [26]       -
> +  15 [0f]    /WPT    [28]   /DSKCHG [34]  /TRK00  [26]
> +-------------------------------------------------------
> +
> +In addition, the same modifiers can be applied as for the
> +output.
> +
> +
> +The following (minimal) circuit is not suited to be used
> +together with a Floppy drive (which would need some logic
> +to work properly) but as replacement.
> +
> + +---+
> + | F |  Motor Enable A [/MOTEA,10] -------------- SCL
> + | L |
> + | O |  Motor Enable B [/MOTEB,16] ------+------- SDA
> + | P |                                   |
> + | P |                                   |
> + | Y |  Disk Change   [/DSKCHG,34] ------+    +-- GND
> + +-+-+                                        |
> +   |                                          |
> +  GND                                             GND

This apparently corresponds to mode 1/1, you may want to mention it.

> +
> +The Motor Enable output of floppy disk controllers is an open
> +drain output (48mA), usually pulled up to +5V via 1k or higher,
> +and the Disk Change is a Schmitt Trigger (0.8V/2.2V) input with
> +max 150uA input current (data taken from the WD/FDC 37C6xx
> +Floppy Disk Subsystem Controller datasheets, which basically
> +combine all the necessary parts of the PC floppy circuit).
> +
> +So it should be fine to connect it to all 5V I2C devices, and
> +most 3.3V devices (which are usually capable of handling 5V
> +I2C bus voltages).
> +
> +Power for those devices can be drawn from either the Floppy
> +power connector (+5V/+12V) or from the SATA 3.3V lines
> +(or if you prefer from some external source).
> +
> +
> +Here is a slightly more advanced circuit, which works on
> +controllers with reduced functionality (i.e. without control
> +lines for a second drive), while allowing for SCL readback
> +(and thus clock stretching):
> +
> +
> + +---+
> + |   |  Motor Enable A [/MOTEA,10] -------------- ENA
> + | F |
> + | L |  Drive Select A [/DRVSA,14] ------+------- SDA
> + | O |                                   |
> + | P |  Write Protect  [/WPT,28] --------+
> + | P |
> + | Y |  Head Select    [/SIDE1,32] ------+------- SCL
> + |   |                                   |
> + |   |  Disk Change    [/DSKCHG,34] -----+    +-- GND
> + +-+-+                                        |
> +   |                                          |
> +  GND                                             GND

And this is mode 16/6 with SDA and SCL swapped?

As I already said on IRC, my personal feeling is that this complexity
is overkill, and you could basically have only 2 or 3 modes, like the
i2c-parport-light driver is doing. At least you would be able to test
them and ensure that they really work, while with the above, you just
can't test all combinations, so I expect surprises. In particular, what
will happen, for every possible combination, if your driver is loaded
while a real floppy disk driver is connected? Can you guarantee that no
harm will be done to the hardware and no data will be lost?

> +
> diff -NurpP --minimal linux-2.6.27-rc8/drivers/i2c/busses/i2c-floppy.c 
> linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/i2c-floppy.c
> --- linux-2.6.27-rc8/drivers/i2c/busses/i2c-floppy.c  1970-01-01 
> 01:00:00.000000000 +0100
> +++ linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/i2c-floppy.c      
> 2008-10-01 18:34:32.000000000 +0200
> @@ -0,0 +1,626 @@
> +/* ------------------------------------------------------------------------ *
> + * i2c-floppy.c I2C bus over floppy controller                              *
> + * ------------------------------------------------------------------------ *
> +   Copyright (C) 2008 Herbert Poetzl <[EMAIL PROTECTED]>
> +
> +   Somewhat based on i2c-parport-light.c driver
> +   Copyright (C) 2003-2007 Jean Delvare <[EMAIL PROTECTED]>
> +
> +   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 <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/ioport.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-algo-bit.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +
> +static struct platform_device *pdev;
> +static unsigned char dor;
> +
> +static u16 base;
> +module_param(base, ushort, 0);
> +MODULE_PARM_DESC(base, "Base I/O address");
> +
> +static u16 delay;
> +module_param(delay, ushort, 0);
> +MODULE_PARM_DESC(base, "Signal delay");

What is this supposed to be? "Signal delay" doesn't mean anything to
me. I suspect you mean half-period of the I2C clock expressed in us?

> +
> +
> +#define DEFAULT_BASE 0x3F0           /* for PC style hardware */
> +#define DRVNAME              "i2c-floppy"
> +
> +
> +#define FOFF_DOR     0x02
> +#define FOFF_MSR     0x04
> +#define FOFF_DATA    0x05
> +#define FOFF_CCR     0x07

You don't use this define anywhere, so why define it? (Also it looks
strange that FOFF_CCR and FOFF_DIR have the same value.)

> +#define FOFF_DIR     0x07
> +
> +
> +#define      FDOR_MOA        0x10
> +#define      FDOR_MOB        0x20
> +#define      FDOR_DS0        0x01
> +#define      FDOR_DS1        0x02
> +
> +#define      FDOR_RESET      0x04

Why is there a blank line before that one? Is it special?

> +
> +#define      FDIR_CHG        0x80
> +
> +#define      FMSR_RQM        0x80
> +#define      FMSR_DIO        0x40
> +#define      FMSR_CMD        0x10
> +
> +#define      FCMD_SENSE      0x04
> +#define      FCMS_HDS        0x04
> +
> +#define      FST3_T0         0x10
> +#define      FST3_WP         0x40
> +
> +
> +/* ----- Low-level floppy access ------------------------------------------ 
> */
> +
> +static inline void port_dor_out(unsigned char d)
> +{
> +     outb(d, base + FOFF_DOR);
> +}
> +
> +static inline unsigned char port_dir_in(void)
> +{
> +     return inb(base + FOFF_DIR);
> +}
> +
> +static inline unsigned char port_msr_in(void)
> +{
> +     return inb(base + FOFF_MSR);
> +}
> +
> +static inline void port_data_out(unsigned char d)
> +{
> +     outb(d, base + FOFF_DATA);
> +}
> +
> +static inline unsigned char port_data_in(void)
> +{
> +     return inb(base + FOFF_DATA);
> +}
> +
> +
> +/* ----- Floppy controller sense ------------------------------------------ 
> */
> +
> +
> +static inline unsigned char msr_wait(
> +     unsigned mask, unsigned val, int timeout)

Odd indentation.

> +{
> +     unsigned char st;
> +
> +     /* wait for controller */
> +     while (((st = port_msr_in()) & mask) != val)
> +             if (!--timeout)
> +                     break;
> +
> +     if (timeout)
> +             return st;
> +
> +     pr_err(DRVNAME ": timeout reached ... %02x/%02x\n", dor, st);
> +     return 0;
> +}
> +
> +unsigned char cmd_sense(unsigned char head)

Should be static.

> +{
> +     unsigned char st;
> +
> +     /* wait for controller */
> +     msr_wait(FMSR_RQM|FMSR_CMD, FMSR_RQM, 5000);
> +
> +     port_data_out(FCMD_SENSE);
> +     st = msr_wait(FMSR_RQM, FMSR_RQM, 5000);
> +
> +     if (!(st & FMSR_CMD))
> +             return 0;

The usual coding style is no blank line before a function call and
testing the result.

> +
> +     port_data_out(head ? FCMS_HDS : 0);
> +     st = msr_wait(FMSR_RQM, FMSR_RQM, 5000);
> +
> +     if (!(st & FMSR_CMD))
> +             return 0;
> +
> +     return port_data_in();
> +}
> +
> +
> +/* ----- Output and input lines ------------------------------------------- 
> */
> +
> +#define      LINE_OFF        0x00
> +
> +     /* output lines */
> +
> +#define      LINE_MOA        FDOR_MOA
> +#define      LINE_MOB        FDOR_MOB
> +#define      LINE_DS0        FDOR_DS0
> +#define      LINE_DS1        FDOR_DS1
> +
> +#define      LINE_MASK_DOR   (LINE_MOA | LINE_MOB | LINE_DS0 | LINE_DS1)
> +
> +#define      LINE_HDS        0x08
> +
> +struct i2c_floppy_lines {
> +     unsigned ena:8;
> +     unsigned sda:8;
> +     unsigned scl:8;

Err, why don't you just use 3 unsigned chars? I suspect this would be
more efficient.

> +};
> +
> +static struct i2c_floppy_lines i2c_floppy_lines_out[] = {
> +     { LINE_OFF, LINE_OFF, LINE_OFF },       /*  0 */
> +
> +     { LINE_OFF, LINE_MOA, LINE_MOB },       /*  1 */
> +     { LINE_HDS, LINE_MOA, LINE_MOB },       /*  2 */
> +
> +     { LINE_OFF, LINE_MOA, LINE_DS1 },       /*  3 */
> +     { LINE_MOB, LINE_MOA, LINE_DS1 },       /*  4 */
> +     { LINE_HDS, LINE_MOA, LINE_DS1 },       /*  5 */
> +
> +     { LINE_OFF, LINE_MOA, LINE_HDS },       /*  6 */
> +     { LINE_MOB, LINE_MOA, LINE_HDS },       /*  7 */
> +     { LINE_DS1, LINE_MOA, LINE_HDS },       /*  8 */
> +
> +     { LINE_OFF, LINE_MOB, LINE_DS0 },       /*  9 */
> +     { LINE_MOA, LINE_MOB, LINE_DS0 },       /* 10 */
> +     { LINE_HDS, LINE_MOB, LINE_DS0 },       /* 11 */
> +
> +     { LINE_OFF, LINE_MOB, LINE_HDS },       /* 12 */
> +     { LINE_MOA, LINE_MOB, LINE_HDS },       /* 13 */
> +     { LINE_DS0, LINE_MOB, LINE_HDS },       /* 14 */
> +
> +     { LINE_OFF, LINE_HDS, LINE_DS0 },       /* 15 */
> +     { LINE_MOA, LINE_HDS, LINE_DS0 },       /* 16 */
> +     { LINE_MOB, LINE_HDS, LINE_DS0 },       /* 17 */
> +     { LINE_DS1, LINE_HDS, LINE_DS0 },       /* 18 */
> +
> +     { LINE_OFF, LINE_HDS, LINE_DS1 },       /* 19 */
> +     { LINE_MOA, LINE_HDS, LINE_DS1 },       /* 20 */
> +     { LINE_MOB, LINE_HDS, LINE_DS1 },       /* 21 */
> +     { LINE_DS0, LINE_HDS, LINE_DS1 },       /* 22 */
> +};
> +
> +     /* input lines */
> +
> +#define      LINE_T0         FST3_T0
> +#define      LINE_WP         FST3_WP
> +
> +#define      LINE_MASK_ST3   (LINE_T0 | LINE_WP)
> +
> +#define LINE_CHG     FDIR_CHG
> +
> +static struct i2c_floppy_lines i2c_floppy_lines_in[] = {
> +     { LINE_OFF, LINE_OFF, LINE_OFF },       /*  0 - auto*/

Missing space at end of comment.

> +
> +     { LINE_OFF, LINE_CHG, LINE_OFF },       /*  1 */
> +     { LINE_OFF, LINE_T0,  LINE_OFF },       /*  2 */
> +     { LINE_OFF, LINE_WP,  LINE_OFF },       /*  3 */
> +
> +     { LINE_OFF, LINE_CHG, LINE_T0  },       /*  4 */
> +     { LINE_OFF, LINE_T0,  LINE_WP  },       /*  5 */
> +     { LINE_OFF, LINE_WP,  LINE_CHG },       /*  6 */
> +
> +     { LINE_CHG, LINE_T0,  LINE_OFF },       /*  7 */
> +     { LINE_CHG, LINE_WP,  LINE_OFF },       /*  8 */
> +     { LINE_CHG, LINE_T0,  LINE_WP  },       /*  9 */
> +
> +     { LINE_T0,  LINE_CHG, LINE_OFF },       /* 10 */
> +     { LINE_T0,  LINE_WP,  LINE_OFF },       /* 11 */
> +     { LINE_T0,  LINE_WP,  LINE_CHG },       /* 12 */
> +
> +     { LINE_WP,  LINE_CHG, LINE_OFF },       /* 13 */
> +     { LINE_WP,  LINE_T0,  LINE_OFF },       /* 14 */
> +     { LINE_WP,  LINE_CHG, LINE_T0  },       /* 15 */
> +};
> +
> +     /* parameters */
> +
> +#define      LM_SWAP         0x0020
> +#define      LM_INV_SDA      0x0040
> +#define      LM_INV_SCL      0x0080
> +#define      LM_INV_ENA      0x0100
> +
> +
> +static unsigned int line_mode = 0xe1;

I would rather not default to a specific wiring. To make sure that the
user knows what he/she is doing, it's better to have him/her pass the
parameter mode he/she needs. This is what the i2c-parport-light driver
is doing.

> +module_param(line_mode, uint, 0);
> +MODULE_PARM_DESC(line_mode, "Output line mode");
> +
> +static unsigned int line_mode_in = 0x00;

Do not initialize static variables to 0, let the compiler do it for you.

> +module_param(line_mode_in, uint, 0);
> +MODULE_PARM_DESC(line_mode_in, "Input line mode");
> +
> +
> +     /* calculated */
> +
> +static unsigned char lo_inv;
> +static unsigned char lo_mask;
> +
> +static unsigned char li_inv;
> +static unsigned char li_mask;
> +
> +static struct i2c_floppy_lines lines;
> +static struct i2c_floppy_lines lines_in;
> +
> +
> +/* ----- Output and input helpers ----------------------------------------- 
> */
> +
> +static unsigned char i2c_lo;

What is this one for? This deserves a comment.

> +
> +static void update_lo(unsigned char lo)
> +{
> +     static unsigned char lo_prev;
> +     unsigned char chg = lo ^ lo_prev;
> +
> +     if (!chg)
> +             return;
> +
> +     lo_prev = lo;
> +
> +     if (chg & LINE_MASK_DOR) {
> +             dor &= ~LINE_MASK_DOR;
> +             dor |= (lo ^ lo_inv) & LINE_MASK_DOR;
> +             port_dor_out(dor);
> +     }
> +
> +     if (chg & LINE_HDS)
> +             cmd_sense((lo ^ lo_inv) & LINE_HDS);
> +}
> +
> +
> +static unsigned char i2c_li;

Same here, especially when you never use the value of this variable.

> +
> +static unsigned char update_li(void)
> +{
> +     unsigned char li = 0;
> +
> +     /* conditionally retrieve st3 flags */
> +     if (li_mask & LINE_MASK_ST3)
> +             li |= cmd_sense((i2c_lo ^ lo_inv) & LINE_HDS)
> +                     & LINE_MASK_ST3;
> +
> +     /* conditionally map in disk change */
> +     if (li_mask & LINE_CHG)
> +             li |= port_dir_in() & FDIR_CHG;
> +
> +     i2c_li = li;
> +     return li;
> +}
> +
> +
> +/* ----- I2C algorithm call-back functions and structures ----------------- 
> */
> +
> +static void floppy_setena(void *data, int state)
> +{
> +     if (state)
> +             i2c_lo |= lines.ena;
> +     else
> +             i2c_lo &= ~lines.ena;
> +
> +     update_lo(i2c_lo);
> +}
> +
> +static void floppy_setscl(void *data, int state)
> +{
> +     if (state)
> +             i2c_lo |= lines.scl;
> +     else
> +             i2c_lo &= ~lines.scl;
> +
> +     update_lo(i2c_lo);
> +}
> +
> +static void floppy_setsda(void *data, int state)
> +{
> +     if (state)
> +             i2c_lo |= lines.sda;
> +     else
> +             i2c_lo &= ~lines.sda;
> +
> +     update_lo(i2c_lo);
> +}
> +
> +
> +static int floppy_getena(void *data)
> +{
> +     return (update_li() ^ li_inv) & lines_in.ena;
> +}
> +
> +static int floppy_getscl(void *data)
> +{
> +     return (update_li() ^ li_inv) & lines_in.scl;
> +}
> +
> +static int floppy_getsda(void *data)
> +{
> +     return (update_li() ^ li_inv) & lines_in.sda;
> +}
> +
> +
> +static struct i2c_algo_bit_data floppy_algo_data = {
> +     .setsda         = floppy_setsda,
> +     .setscl         = floppy_setscl,
> +     .getsda         = floppy_getsda,
> +     .getscl         = floppy_getscl,
> +     .udelay         = 50,
> +     .timeout        = HZ,
> +};
> +
> +
> +/* ----- Driver setup ----------------------------------------------------- 
> */
> +
> +static void swap_sda_scl(struct i2c_floppy_lines *lines)
> +{
> +     unsigned char tmp = lines->sda;
> +
> +     lines->sda = lines->scl;
> +     lines->scl = tmp;
> +}
> +
> +
> +static void setup_lo(void)
> +{
> +     /* avoid undefined line modes */
> +     if ((line_mode & 0x1F) > 22)

Please use ARRAY_SIZE(i2c_floppy_lines_out).

> +             line_mode &= ~0x1F;

Bad idea. Better return an error and bail out. If the user asked for a
mode that doesn't exist, he/she messed up, and silently defaulting to
an arbitrary mode has little chance to work.

> +
> +     lines = i2c_floppy_lines_out[line_mode & 0x1F];
> +
> +     /* swap scl and sda */
> +     if (line_mode & LM_SWAP)
> +             swap_sda_scl(&lines);
> +
> +     if (line_mode & LM_INV_SDA)
> +             lo_inv ^= lines.sda;

Wouldn't it be more intuitive to use |= here?

> +     if (line_mode & LM_INV_SCL)
> +             lo_inv ^= lines.scl;
> +     if (line_mode & LM_INV_ENA)
> +             lo_inv ^= lines.ena;
> +
> +     /* calculate line mask */
> +     lo_mask = lines.ena | lines.sda | lines.scl;
> +
> +     pr_info(DRVNAME ": line output config: "
> +             "%02x/%02x/%02x [%02x/%02x]\n",
> +             lines.ena, lines.scl, lines.sda, lo_mask, lo_inv);

This message isn't too helpful, as you have to look at the source code
to figure out which value corresponds to what. Why don't you put the
names in the message? "ENA=%02x, SCL=%02x, ..."

> +}
> +
> +
> +static unsigned char lo_state(int ena, int sda, int scl)
> +{
> +     floppy_setena(NULL, ena);
> +     floppy_setsda(NULL, sda);
> +     floppy_setscl(NULL, scl);
> +     return update_li();
> +}
> +
> +static void auto_setup_li(void)

This function clearly deserves a comment explaining what you are doing,
why you are doing it, and why it is safe to do it.

> +{
> +     unsigned char a, b, m;

More explicit variable names wouldn't hurt.

> +
> +     li_mask = ~0;
> +     li_inv = 0;
> +
> +     /* reset controller */
> +     dor = 0;
> +     port_dor_out(dor);
> +
> +     /* enable controller */
> +     dor |= FDOR_RESET;
> +     port_dor_out(dor);
> +
> +     udelay(100);
> +
> +     a = lo_state(0, 1, 1);
> +     b = lo_state(1, 1, 1);
> +     m = a ^ b;      /* maybe only one bit? */

I don't understand this comment.

> +
> +     /* calculate enable input */
> +     lines_in.ena = m;
> +     li_inv |= a & m;
> +
> +     a = b;
> +     b = lo_state(1, 1, 0);
> +     m = a ^ b;      /* maybe only one bit? */
> +
> +     /* calculate scl input */
> +     lines_in.scl = m;
> +     li_inv |= b & m;
> +
> +     a = b;
> +     b = lo_state(1, 0, 0);
> +     m = a ^ b;      /* maybe only one bit? */
> +
> +     /* calculate sda input */
> +     lines_in.sda = m;
> +     li_inv |= b & m;
> +}

This assumes that nobody is holding the I2C lines (excluding
multi-master setups.) This should be mentioned. BTW, I am surprised
that this function can't fail? What if the line change you expect
doesn't happen? This should be handled.

> +
> +static void setup_li(void)
> +{
> +     /* automatic setup if selected */
> +     if (!line_mode_in) {
> +             auto_setup_li();
> +             goto calc_mask;
> +     }
> +
> +     lines_in = i2c_floppy_lines_in[line_mode_in & 0x0F];

You should test line_mode_in against ARRAY_SIZE(i2c_floppy_lines_in).
It happens that this value is a power of 2 right now, but this is by
chance, not design, and may not always be the case in the future.

> +
> +     /* swap scl and sda */
> +     if (line_mode_in & LM_SWAP)
> +             swap_sda_scl(&lines_in);
> +
> +     if (line_mode_in & LM_INV_SDA)
> +             li_inv ^= lines_in.sda;
> +     if (line_mode_in & LM_INV_SCL)
> +             li_inv ^= lines_in.scl;
> +     if (line_mode_in & LM_INV_ENA)
> +             li_inv ^= lines_in.ena;
> +
> +calc_mask:
> +     /* calculate line mask */
> +     li_mask = lines_in.ena | lines_in.sda | lines_in.scl;
> +
> +     pr_info(DRVNAME ": line input config:  "
> +             "%02x/%02x/%02x [%02x/%02x]\n",
> +             lines_in.ena, lines_in.scl, lines_in.sda,
> +             li_mask, li_inv);
> +}

Most of my comments on setup_lo() apply here as well.

> +
> +
> +/* ----- Driver registration ---------------------------------------------- 
> */
> +
> +static struct i2c_adapter floppy_adapter = {
> +     .owner          = THIS_MODULE,
> +     .class          = I2C_CLASS_HWMON,
> +     .algo_data      = &floppy_algo_data,
> +     .name           = "Floppy controller adapter",
> +};
> +
> +static int __devinit i2c_floppy_probe(struct platform_device *pdev)
> +{
> +     int err;
> +
> +     floppy_adapter.dev.parent = &pdev->dev;
> +     err = i2c_bit_add_bus(&floppy_adapter);
> +     if (err)
> +             dev_err(&pdev->dev, "Unable to register with I2C\n");
> +     return err;
> +}
> +
> +static int __devexit i2c_floppy_remove(struct platform_device *pdev)
> +{
> +     i2c_del_adapter(&floppy_adapter);
> +     return 0;
> +}
> +
> +static struct platform_driver i2c_floppy_driver = {
> +     .driver = {
> +             .owner  = THIS_MODULE,
> +             .name   = DRVNAME,
> +     },
> +     .probe          = i2c_floppy_probe,
> +     .remove         = __devexit_p(i2c_floppy_remove),
> +};
> +
> +static int __init i2c_floppy_device_add(u16 address)
> +{
> +     int err;
> +
> +     pdev = platform_device_alloc(DRVNAME, -1);
> +     if (!pdev) {
> +             err = -ENOMEM;
> +             pr_err(DRVNAME ": Device allocation failed\n");
> +             goto exit;
> +     }
> +
> +     err = platform_device_add(pdev);
> +     if (err) {
> +             pr_err(DRVNAME ": Device addition failed (%d)\n",
> +                    err);
> +             goto exit_device_put;
> +     }
> +
> +     return 0;
> +
> +exit_device_put:
> +     platform_device_put(pdev);
> +exit:
> +     return err;
> +}
> +
> +static int __init i2c_floppy_init(void)
> +{
> +     int err;
> +
> +     if (base == 0) {
> +             pr_info(DRVNAME ": using default base 0x%x\n",
> +                     DEFAULT_BASE);
> +             base = DEFAULT_BASE;
> +     }
> +
> +     err = -EBUSY;
> +     if (!request_region(base, 5, DRVNAME))
> +             goto exit;
> +
> +     if (!request_region(base + 7, 1, DRVNAME))
> +             goto exit_rel0;

This gives you access to offsets 0, 1, 2, 3, 4 and 7, while your code
accesses offsets 2, 4, 5 and 7. Seems incorrect.

> +
> +     /* Sets global pdev as a side effect */
> +     err = i2c_floppy_device_add(base);
> +     if (err)
> +             goto exit_rel7;
> +
> +     err = platform_driver_register(&i2c_floppy_driver);
> +     if (err)
> +             goto exit_device;
> +
> +     setup_lo();
> +     setup_li();

You are doing things in the wrong order: everything should be setup by
the time i2c_bit_add_adapter() is called. Otherwise there's a
possibility that someone (e.g. a hwmon kernel driver) starts using the
adapter before it's ready.

It's unclear to me why you want to setup the adapter here in
i2c_floppy_init() rather than in i2c_floppy_probe(), where it would be
way clearer in which order things happen.

> +
> +     dor &= ~FDOR_RESET;
> +     /* do we need the controller active? */
> +     if ((li_mask & LINE_MASK_ST3) ||
> +             (lo_mask & LINE_HDS))

Should fit on a single line.

> +             dor |= FDOR_RESET;
> +     port_dor_out(dor);
> +
> +     lo_state(1, 1, 1);
> +     pr_info(DRVNAME ": input line check %02x/%02x/%02x\n",
> +             floppy_getena(NULL),
> +             floppy_getscl(NULL),
> +             floppy_getsda(NULL));

Same comment as the other pr_info.

> +
> +     /* disable getscl if no mapping */
> +     if (!lines_in.scl)
> +             floppy_algo_data.getscl = NULL;
> +
> +     /* update udelay with param */
> +     floppy_algo_data.udelay = delay;

delay is 0 by default, this will break. values of delay < 5 would
better be rejected, as I doubt they will work.

> +
> +     return 0;
> +
> +exit_device:
> +     platform_device_unregister(pdev);
> +exit_rel7:
> +     release_region(base + 7, 1);
> +exit_rel0:
> +     release_region(base, 5);
> +exit:
> +     return err;
> +}
> +
> +static void __exit i2c_floppy_exit(void)
> +{
> +     platform_driver_unregister(&i2c_floppy_driver);
> +     platform_device_unregister(pdev);
> +     release_region(base + 7, 1);
> +     release_region(base, 5);
> +}
> +
> +
> +MODULE_AUTHOR("Herbert Poetzl <[EMAIL PROTECTED]>");
> +MODULE_DESCRIPTION("I2C bus over floppy controller");
> +MODULE_LICENSE("GPL");
> +
> +module_init(i2c_floppy_init);
> +module_exit(i2c_floppy_exit);
> diff -NurpP --minimal linux-2.6.27-rc8/drivers/i2c/busses/Kconfig 
> linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/Kconfig
> --- linux-2.6.27-rc8/drivers/i2c/busses/Kconfig       2008-09-30 
> 22:53:59.000000000 +0200
> +++ linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/Kconfig   2008-09-30 
> 22:57:12.000000000 +0200
> @@ -490,6 +490,21 @@ config I2C_VERSATILE
>  
>  comment "External I2C/SMBus adapter drivers"
>  
> +config I2C_FLOPPY
> +     tristate "Floppy controller adapter"
> +     select I2C_ALGOBIT
> +     help
> +       This supports a simple do-it-yourself floppy controller to
> +       I2C adapters using the motor control lines for SDA and SCL,
> +       and the drive change input for SDA readback.

This description is now a bit too specific given that the driver can
actually use a variety of combination for input and output lines.

> +
> +       This support is also available as a module.  If so, the module
> +       will be called i2c-floppy.
> +
> +       If you do not have such a device, and do not plan to build one,
> +       it's safe to say N here. Do not say Y here and to the floppy
> +       driver unless you know exactly what you are doing.

As explained before, this can be enforced by adding:

depends on !BLK_DEV_FD

I have a patch doing that and I will push it together with yours if
needed, but it would make more sense to merge it directly in your patch.

I would also like you to add a dependency on EXPERIMENTAL, as this is
what your driver is at the moment.

> +
>  config I2C_PARPORT
>       tristate "Parallel port adapter"
>       depends on PARPORT
> diff -NurpP --minimal linux-2.6.27-rc8/drivers/i2c/busses/Makefile 
> linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/Makefile
> --- linux-2.6.27-rc8/drivers/i2c/busses/Makefile      2008-09-30 
> 22:53:59.000000000 +0200
> +++ linux-2.6.27-rc8-fi2c-v0.6.1/drivers/i2c/busses/Makefile  2008-09-30 
> 22:56:14.000000000 +0200
> @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_SIMTEC)    += i2c-simtec.o
>  obj-$(CONFIG_I2C_VERSATILE)  += i2c-versatile.o
>  
>  # External I2C/SMBus adapter drivers
> +obj-$(CONFIG_I2C_FLOPPY)     += i2c-floppy.o
>  obj-$(CONFIG_I2C_PARPORT)    += i2c-parport.o
>  obj-$(CONFIG_I2C_PARPORT_LIGHT)      += i2c-parport-light.o
>  obj-$(CONFIG_I2C_TAOS_EVM)   += i2c-taos-evm.o
> diff -NurpP --minimal linux-2.6.27-rc8/Makefile 
> linux-2.6.27-rc8-fi2c-v0.6.1/Makefile
> --- linux-2.6.27-rc8/Makefile 2008-09-30 22:53:29.000000000 +0200
> +++ linux-2.6.27-rc8-fi2c-v0.6.1/Makefile     2008-10-01 18:43:54.000000000 
> +0200
> @@ -1,7 +1,7 @@
>  VERSION = 2
>  PATCHLEVEL = 6
>  SUBLEVEL = 27
> -EXTRAVERSION = -rc8
> +EXTRAVERSION = -rc8linux-2.6.27-rc8-fi2c-v0.6.1
>  NAME = Rotary Wombat
>  
>  # *DOCUMENTATION*

This last chunk was probably not meant to be included in the patch.

OTOH, your driver has reached a level of complexity such that I do not
want to be the one maintaining it. So, if you want it in mainline,
you'll have to add an entry in MAINTAINERS for this driver with your
name on it.

-- 
Jean Delvare

_______________________________________________
i2c mailing list
i2c@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/i2c

Reply via email to