Commit d369c918 authored by Rui Miguel Silva's avatar Rui Miguel Silva Committed by Greg Kroah-Hartman

usb: isp1763: add peripheral mode

Besides the already host mode support add peripheral mode support for
the isp1763 IP from the isp1760 family.
Signed-off-by: default avatarRui Miguel Silva <rui.silva@linaro.org>
Link: https://lore.kernel.org/r/20210513084717.2487366-10-rui.silva@linaro.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e7a990e0
...@@ -83,7 +83,8 @@ static int isp1760_init_core(struct isp1760_device *isp) ...@@ -83,7 +83,8 @@ static int isp1760_init_core(struct isp1760_device *isp)
* *
* TODO: Really support OTG. For now we configure port 1 in device mode * TODO: Really support OTG. For now we configure port 1 in device mode
*/ */
if ((isp->devflags & ISP1760_FLAG_ISP1761) && if (((isp->devflags & ISP1760_FLAG_ISP1761) ||
(isp->devflags & ISP1760_FLAG_ISP1763)) &&
(isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) { (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) {
isp1760_field_set(hcd->fields, HW_DM_PULLDOWN); isp1760_field_set(hcd->fields, HW_DM_PULLDOWN);
isp1760_field_set(hcd->fields, HW_DP_PULLDOWN); isp1760_field_set(hcd->fields, HW_DP_PULLDOWN);
...@@ -470,13 +471,15 @@ static const struct regmap_config isp1763_dc_regmap_conf = { ...@@ -470,13 +471,15 @@ static const struct regmap_config isp1763_dc_regmap_conf = {
int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
struct device *dev, unsigned int devflags) struct device *dev, unsigned int devflags)
{ {
bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
const struct regmap_config *hc_regmap; const struct regmap_config *hc_regmap;
const struct reg_field *hc_reg_fields; const struct reg_field *hc_reg_fields;
const struct regmap_config *dc_regmap;
const struct reg_field *dc_reg_fields;
struct isp1760_device *isp; struct isp1760_device *isp;
struct isp1760_hcd *hcd; struct isp1760_hcd *hcd;
struct isp1760_udc *udc; struct isp1760_udc *udc;
struct regmap_field *f; struct regmap_field *f;
bool udc_enabled;
int ret; int ret;
int i; int i;
...@@ -484,8 +487,11 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ...@@ -484,8 +487,11 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
* If neither the HCD not the UDC is enabled return an error, as no * If neither the HCD not the UDC is enabled return an error, as no
* device would be registered. * device would be registered.
*/ */
udc_enabled = ((devflags & ISP1760_FLAG_ISP1763) ||
(devflags & ISP1760_FLAG_ISP1761));
if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) && if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
(!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled)) (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || !udc_enabled))
return -ENODEV; return -ENODEV;
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
...@@ -498,6 +504,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ...@@ -498,6 +504,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
udc = &isp->udc; udc = &isp->udc;
hcd->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763); hcd->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763);
udc->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763);
if (!hcd->is_isp1763 && (devflags & ISP1760_FLAG_BUS_WIDTH_8)) { if (!hcd->is_isp1763 && (devflags & ISP1760_FLAG_BUS_WIDTH_8)) {
dev_err(dev, "isp1760/61 do not support data width 8\n"); dev_err(dev, "isp1760/61 do not support data width 8\n");
...@@ -507,9 +514,13 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ...@@ -507,9 +514,13 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
if (hcd->is_isp1763) { if (hcd->is_isp1763) {
hc_regmap = &isp1763_hc_regmap_conf; hc_regmap = &isp1763_hc_regmap_conf;
hc_reg_fields = &isp1763_hc_reg_fields[0]; hc_reg_fields = &isp1763_hc_reg_fields[0];
dc_regmap = &isp1763_dc_regmap_conf;
dc_reg_fields = &isp1763_dc_reg_fields[0];
} else { } else {
hc_regmap = &isp1760_hc_regmap_conf; hc_regmap = &isp1760_hc_regmap_conf;
hc_reg_fields = &isp1760_hc_reg_fields[0]; hc_reg_fields = &isp1760_hc_reg_fields[0];
dc_regmap = &isp1761_dc_regmap_conf;
dc_reg_fields = &isp1761_dc_reg_fields[0];
} }
isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
...@@ -532,14 +543,12 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ...@@ -532,14 +543,12 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
hcd->fields[i] = f; hcd->fields[i] = f;
} }
udc->regs = devm_regmap_init_mmio(dev, hcd->base, udc->regs = devm_regmap_init_mmio(dev, hcd->base, dc_regmap);
&isp1761_dc_regmap_conf);
if (IS_ERR(udc->regs)) if (IS_ERR(udc->regs))
return PTR_ERR(udc->regs); return PTR_ERR(udc->regs);
for (i = 0; i < DC_FIELD_MAX; i++) { for (i = 0; i < DC_FIELD_MAX; i++) {
f = devm_regmap_field_alloc(dev, udc->regs, f = devm_regmap_field_alloc(dev, udc->regs, dc_reg_fields[i]);
isp1761_dc_reg_fields[i]);
if (IS_ERR(f)) if (IS_ERR(f))
return PTR_ERR(f); return PTR_ERR(f);
...@@ -562,7 +571,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ...@@ -562,7 +571,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
return ret; return ret;
} }
if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) { if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && udc_enabled) {
ret = isp1760_udc_register(isp, irq, irqflags); ret = isp1760_udc_register(isp, irq, irqflags);
if (ret < 0) { if (ret < 0) {
isp1760_hcd_unregister(hcd); isp1760_hcd_unregister(hcd);
......
...@@ -243,8 +243,50 @@ enum isp176x_device_controller_fields { ...@@ -243,8 +243,50 @@ enum isp176x_device_controller_fields {
DC_EPENABLE, DC_ENDPTYP, DC_EPENABLE, DC_ENDPTYP,
/* DC_FRAMENUM */ /* DC_FRAMENUM */
DC_FRAMENUM, DC_UFRAMENUM, DC_FRAMENUM, DC_UFRAMENUM,
/* DC_CHIP_ID */
DC_CHIP_ID_HIGH, DC_CHIP_ID_LOW,
/* DC_SCRATCH */
DC_SCRATCH,
/* Last element */ /* Last element */
DC_FIELD_MAX, DC_FIELD_MAX,
}; };
/* ISP1763 */
/* Initialization Registers */
#define ISP1763_DC_ADDRESS 0x00
#define ISP1763_DC_MODE 0x0c
#define ISP1763_DC_INTCONF 0x10
#define ISP1763_DC_INTENABLE 0x14
/* Data Flow Registers */
#define ISP1763_DC_EPMAXPKTSZ 0x04
#define ISP1763_DC_EPTYPE 0x08
#define ISP1763_DC_BUFLEN 0x1c
#define ISP1763_DC_BUFSTAT 0x1e
#define ISP1763_DC_DATAPORT 0x20
#define ISP1763_DC_CTRLFUNC 0x28
#define ISP1763_DC_EPINDEX 0x2c
/* DMA Registers */
#define ISP1763_DC_DMACMD 0x30
#define ISP1763_DC_DMATXCOUNT 0x34
#define ISP1763_DC_DMACONF 0x38
#define ISP1763_DC_DMAHW 0x3c
#define ISP1763_DC_DMAINTREASON 0x50
#define ISP1763_DC_DMAINTEN 0x54
#define ISP1763_DC_DMAEP 0x58
#define ISP1763_DC_DMABURSTCOUNT 0x64
/* General Registers */
#define ISP1763_DC_INTERRUPT 0x18
#define ISP1763_DC_CHIPID_LOW 0x70
#define ISP1763_DC_CHIPID_HIGH 0x72
#define ISP1763_DC_FRAMENUM 0x74
#define ISP1763_DC_SCRATCH 0x78
#define ISP1763_DC_UNLOCKDEV 0x7c
#define ISP1763_DC_INTPULSEWIDTH 0x80
#define ISP1763_DC_TESTMODE 0x84
#endif #endif
...@@ -1151,6 +1151,10 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc) ...@@ -1151,6 +1151,10 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc)
static void isp1760_udc_init_hw(struct isp1760_udc *udc) static void isp1760_udc_init_hw(struct isp1760_udc *udc)
{ {
u32 intconf = udc->is_isp1763 ? ISP1763_DC_INTCONF : ISP176x_DC_INTCONF;
u32 intena = udc->is_isp1763 ? ISP1763_DC_INTENABLE :
ISP176x_DC_INTENABLE;
/* /*
* The device controller currently shares its interrupt with the host * The device controller currently shares its interrupt with the host
* controller, the DC_IRQ polarity and signaling mode are ignored. Set * controller, the DC_IRQ polarity and signaling mode are ignored. Set
...@@ -1160,11 +1164,11 @@ static void isp1760_udc_init_hw(struct isp1760_udc *udc) ...@@ -1160,11 +1164,11 @@ static void isp1760_udc_init_hw(struct isp1760_udc *udc)
* ACK tokens only (and NYET for the out pipe). The default * ACK tokens only (and NYET for the out pipe). The default
* configuration also generates an interrupt on the first NACK token. * configuration also generates an interrupt on the first NACK token.
*/ */
isp1760_reg_write(udc->regs, ISP176x_DC_INTCONF, isp1760_reg_write(udc->regs, intconf,
ISP176x_DC_CDBGMOD_ACK | ISP176x_DC_DDBGMODIN_ACK | ISP176x_DC_CDBGMOD_ACK | ISP176x_DC_DDBGMODIN_ACK |
ISP176x_DC_DDBGMODOUT_ACK); ISP176x_DC_DDBGMODOUT_ACK);
isp1760_reg_write(udc->regs, ISP176x_DC_INTENABLE, DC_IEPRXTX(7) | isp1760_reg_write(udc->regs, intena, DC_IEPRXTX(7) |
DC_IEPRXTX(6) | DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(6) | DC_IEPRXTX(5) | DC_IEPRXTX(4) |
DC_IEPRXTX(3) | DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(3) | DC_IEPRXTX(2) | DC_IEPRXTX(1) |
DC_IEPRXTX(0) | ISP176x_DC_IEP0SETUP | DC_IEPRXTX(0) | ISP176x_DC_IEP0SETUP |
...@@ -1304,13 +1308,14 @@ static int isp1760_udc_start(struct usb_gadget *gadget, ...@@ -1304,13 +1308,14 @@ static int isp1760_udc_start(struct usb_gadget *gadget,
static int isp1760_udc_stop(struct usb_gadget *gadget) static int isp1760_udc_stop(struct usb_gadget *gadget)
{ {
struct isp1760_udc *udc = gadget_to_udc(gadget); struct isp1760_udc *udc = gadget_to_udc(gadget);
u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE;
unsigned long flags; unsigned long flags;
dev_dbg(udc->isp->dev, "%s\n", __func__); dev_dbg(udc->isp->dev, "%s\n", __func__);
del_timer_sync(&udc->vbus_timer); del_timer_sync(&udc->vbus_timer);
isp1760_reg_write(udc->regs, ISP176x_DC_MODE, 0); isp1760_reg_write(udc->regs, mode_reg, 0);
spin_lock_irqsave(&udc->lock, flags); spin_lock_irqsave(&udc->lock, flags);
udc->driver = NULL; udc->driver = NULL;
...@@ -1332,15 +1337,30 @@ static const struct usb_gadget_ops isp1760_udc_ops = { ...@@ -1332,15 +1337,30 @@ static const struct usb_gadget_ops isp1760_udc_ops = {
* Interrupt Handling * Interrupt Handling
*/ */
static u32 isp1760_udc_irq_get_status(struct isp1760_udc *udc)
{
u32 status;
if (udc->is_isp1763) {
status = isp1760_reg_read(udc->regs, ISP1763_DC_INTERRUPT)
& isp1760_reg_read(udc->regs, ISP1763_DC_INTENABLE);
isp1760_reg_write(udc->regs, ISP1763_DC_INTERRUPT, status);
} else {
status = isp1760_reg_read(udc->regs, ISP176x_DC_INTERRUPT)
& isp1760_reg_read(udc->regs, ISP176x_DC_INTENABLE);
isp1760_reg_write(udc->regs, ISP176x_DC_INTERRUPT, status);
}
return status;
}
static irqreturn_t isp1760_udc_irq(int irq, void *dev) static irqreturn_t isp1760_udc_irq(int irq, void *dev)
{ {
struct isp1760_udc *udc = dev; struct isp1760_udc *udc = dev;
unsigned int i; unsigned int i;
u32 status; u32 status;
status = isp1760_reg_read(udc->regs, ISP176x_DC_INTERRUPT) status = isp1760_udc_irq_get_status(udc);
& isp1760_reg_read(udc->regs, ISP176x_DC_INTENABLE);
isp1760_reg_write(udc->regs, ISP176x_DC_INTERRUPT, status);
if (status & DC_IEVBUS) { if (status & DC_IEVBUS) {
dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
...@@ -1475,6 +1495,7 @@ static void isp1760_udc_init_eps(struct isp1760_udc *udc) ...@@ -1475,6 +1495,7 @@ static void isp1760_udc_init_eps(struct isp1760_udc *udc)
static int isp1760_udc_init(struct isp1760_udc *udc) static int isp1760_udc_init(struct isp1760_udc *udc)
{ {
u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE;
u16 scratch; u16 scratch;
u32 chipid; u32 chipid;
...@@ -1484,9 +1505,10 @@ static int isp1760_udc_init(struct isp1760_udc *udc) ...@@ -1484,9 +1505,10 @@ static int isp1760_udc_init(struct isp1760_udc *udc)
* register, and reading the scratch register value back. The chip ID * register, and reading the scratch register value back. The chip ID
* and scratch register contents must match the expected values. * and scratch register contents must match the expected values.
*/ */
isp1760_reg_write(udc->regs, ISP176x_DC_SCRATCH, 0xbabe); isp1760_udc_write(udc, DC_SCRATCH, 0xbabe);
chipid = isp1760_reg_read(udc->regs, ISP176x_DC_CHIPID); chipid = isp1760_udc_read(udc, DC_CHIP_ID_HIGH) << 16;
scratch = isp1760_reg_read(udc->regs, ISP176x_DC_SCRATCH); chipid |= isp1760_udc_read(udc, DC_CHIP_ID_LOW);
scratch = isp1760_udc_read(udc, DC_SCRATCH);
if (scratch != 0xbabe) { if (scratch != 0xbabe) {
dev_err(udc->isp->dev, dev_err(udc->isp->dev,
...@@ -1495,7 +1517,8 @@ static int isp1760_udc_init(struct isp1760_udc *udc) ...@@ -1495,7 +1517,8 @@ static int isp1760_udc_init(struct isp1760_udc *udc)
return -ENODEV; return -ENODEV;
} }
if (chipid != 0x00011582 && chipid != 0x00158210) { if (chipid != 0x00011582 && chipid != 0x00158210 &&
chipid != 0x00176320) {
dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
return -ENODEV; return -ENODEV;
} }
...@@ -1503,7 +1526,7 @@ static int isp1760_udc_init(struct isp1760_udc *udc) ...@@ -1503,7 +1526,7 @@ static int isp1760_udc_init(struct isp1760_udc *udc)
/* Reset the device controller. */ /* Reset the device controller. */
isp1760_udc_set(udc, DC_SFRESET); isp1760_udc_set(udc, DC_SFRESET);
usleep_range(10000, 11000); usleep_range(10000, 11000);
isp1760_reg_write(udc->regs, ISP176x_DC_MODE, 0); isp1760_reg_write(udc->regs, mode_reg, 0);
usleep_range(10000, 11000); usleep_range(10000, 11000);
return 0; return 0;
......
...@@ -84,6 +84,7 @@ struct isp1760_udc { ...@@ -84,6 +84,7 @@ struct isp1760_udc {
u16 ep0_length; u16 ep0_length;
bool connected; bool connected;
bool is_isp1763;
unsigned int devstatus; unsigned int devstatus;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment