On 04/08/18 09:37, Pawel Laszczak wrote:
>>> Patch adds some functionality for initialization process.
>>> It adds to driver usbssp_reset and usbssp_start function.
>>>
>>> Next elements added are objects usbssp_gadget_ep0_ops,
>>> usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
>>> constitute the interface used by gadget subsystem.
>>> At this moment functions related to these objects are empty
>>> and do nothing.
>>>
>>> This patch also implements usbssp_gadget_init_endpoint and
>>> usbssp_gadget_free_endpoint used during initialization.
>>>
>>> Signed-off-by: Pawel Laszczak <paw...@cadence.com>
>>> ---
>>>  drivers/usb/usbssp/gadget-ext-caps.h |   3 +
>>>  drivers/usb/usbssp/gadget-if.c       | 269 ++++++++++++++++++++++++++-
>>>  drivers/usb/usbssp/gadget.c          |  84 ++++++++-
>>>  3 files changed, 350 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h 
>>> b/drivers/usb/usbssp/gadget-ext-caps.h
>>> index 2bf327046376..86c0ce331037 100644
>>> --- a/drivers/usb/usbssp/gadget-ext-caps.h
>>> +++ b/drivers/usb/usbssp/gadget-ext-caps.h
>>> @@ -51,3 +51,6 @@
>>>  #define USBSSP_CMD_EWE                     BIT(10)
>>>
>>>  #define USBSSP_IRQS        (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | 
>>> USBSSP_CMD_EWE)
>>> +
>>> +/* true: Controller Not Ready to accept doorbell or op reg writes after 
>>> reset */
>>> +#define USBSSP_STS_CNR                     BIT(11)
>>> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
>>> index d53e0fb65299..70def978b085 100644
>>> --- a/drivers/usb/usbssp/gadget-if.c
>>> +++ b/drivers/usb/usbssp/gadget-if.c
>>> @@ -12,13 +12,278 @@
>>>  #include <linux/usb/composite.h>
>>>  #include "gadget.h"
>>>
>>> +static int usbssp_gadget_ep_enable(struct usb_ep *ep,
>>> +                              const struct usb_endpoint_descriptor *desc)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +int usbssp_gadget_ep_disable(struct usb_ep *ep)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep 
>>> *ep,
>>> +                                                     gfp_t gfp_flags)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +
>>> +   if (!ep_priv)
>>> +           return NULL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return NULL;
>>> +}
>>> +
>>> +static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
>>> +                                     struct usb_request *request)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +
>>> +   if (!ep_priv)
>>> +           return;
>>> +
>>> +   /*TODO: implements this function*/
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_queue(struct usb_ep *ep,
>>> +                             struct usb_request *request,
>>> +                             gfp_t gfp_flags)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
>>> +                               struct usb_request *request)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
>>> +{
>>> +   struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +   int ret = 0;
>>> +
>>> +   if (!ep_priv)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: implements this function*/
>>> +   return ret;
>>> +}
>>> +
>>> +static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
>>> +   .enable         = usbssp_gadget_ep_enable,
>>> +   .disable        = usbssp_gadget_ep_disable,
>>> +   .alloc_request  = usbssp_gadget_ep_alloc_request,
>>> +   .free_request   = usbssp_gadget_ep_free_request,
>>> +   .queue          = usbssp_gadget_ep_queue,
>>> +   .dequeue        = usbssp_gadget_ep_dequeue,
>>> +   .set_halt       = usbssp_gadget_ep_set_halt,
>>> +   .set_wedge      = usbssp_gadget_ep_set_wedge,
>>> +};
>>> +
>>> +static const struct usb_ep_ops usbssp_gadget_ep_ops = {
>>> +   .enable         = usbssp_gadget_ep_enable,
>>> +   .disable        = usbssp_gadget_ep_disable,
>>> +   .alloc_request  = usbssp_gadget_ep_alloc_request,
>>> +   .free_request   = usbssp_gadget_ep_free_request,
>>> +   .queue          = usbssp_gadget_ep_queue,
>>> +   .dequeue        = usbssp_gadget_ep_dequeue,
>>> +   .set_halt       = usbssp_gadget_ep_set_halt,
>>> +   .set_wedge      = usbssp_gadget_ep_set_wedge,
>>> +};
>>> +
>>> +static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
>>> +   .bLength =              USB_DT_ENDPOINT_SIZE,
>>> +   .bDescriptorType =      USB_DT_ENDPOINT,
>>> +   .bmAttributes =         USB_ENDPOINT_XFER_CONTROL,
>>> +};
>>> +
>>> +static int usbssp_gadget_start(struct usb_gadget *g,
>>> +                          struct usb_gadget_driver *driver)
>>> +{
>>> +   struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +   int ret = 0;
>>> +
>>> +   if (usbssp_data->gadget_driver) {
>>> +           dev_err(usbssp_data->dev, "%s is already bound to %s\n",
>>> +                           usbssp_data->gadget.name,
>>> +                           usbssp_data->gadget_driver->driver.name);
>>> +           ret = -EBUSY;
>>> +   }
>>> +
>>> +   /*TODO: add implementation*/
>>> +   return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_stop(struct usb_gadget *g)
>>> +{
>>> +   struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> +   if (!usbssp_data)
>>> +           return -EINVAL;
>>> +   /*TODO: add implementation*/
>>> +   return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_get_frame(struct usb_gadget *g)
>>> +{
>>> +   struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> +   if (!usbssp_data)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: add implementation*/
>>> +   return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_wakeup(struct usb_gadget *g)
>>> +{
>>> +   struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> +   if (!usbssp_data)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: add implementation*/
>>> +   return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
>>> +                                    int is_selfpowered)
>>> +{
>>> +   struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> +   if (!usbssp_data)
>>> +           return -EINVAL;
>>> +
>>> +   /*TODO: add implementation*/
>>> +   return 0;
>>> +}
>>> +
>>> +static const struct usb_gadget_ops usbssp_gadget_ops = {
>>> +   .get_frame              = usbssp_gadget_get_frame,
>>> +   .wakeup                 = usbssp_gadget_wakeup,
>>> +   .set_selfpowered        = usbssp_gadget_set_selfpowered,
>>> +   .udc_start              = usbssp_gadget_start,
>>> +   .udc_stop               = usbssp_gadget_stop,
>>> +};
>>> +
>>>  int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
>>
>> Since we're initializing all endpoints this function should be named 
>> usbssp_gadget_init_endpoints().
>>
>>>  {
>>> -   /*TODO: it has to be implemented*/
>>> +   int i = 0;
>>> +   struct usbssp_ep *ep_priv;
>>> +
>>> +   usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
>>> +   INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
>>> +
>>> +   for (i = 1; i < usbssp_data->num_endpoints; i++) {
>>> +           bool direction = i & 1; /*start from OUT endpoint*/
>>> +           u8 epnum = (i >> 1);
>>> +
>>> +           ep_priv = &usbssp_data->devs.eps[i-1];
>>> +           ep_priv->usbssp_data = usbssp_data;
>>> +           ep_priv->number = epnum;
>>> +           ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
>>> +
>>> +           snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
>>> +                           (ep_priv->direction) ? "in" : "out");
>>> +
>>> +           ep_priv->endpoint.name = ep_priv->name;
>>> +
>>> +           if (ep_priv->number < 2) {
>>> +                   ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
>>> +                   ep_priv->endpoint.comp_desc = NULL;
>>> +           }
>>> +
>>> +           if (epnum == 0) {
>>> +                   /*EP0 is bidirectional endpoint*/
>>> +                   usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
>>> +                   dev_dbg(usbssp_data->dev,
>>> +                           "Initializing %s, MaxPack: %04x Type: Ctrl\n",
>>> +                           ep_priv->name, 512);
>>> +                   ep_priv->endpoint.maxburst = 1;
>>> +                   ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
>>> +                   ep_priv->endpoint.caps.type_control = true;
>>> +
>>> +                   usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
>>> +                   usbssp_data->usb_req_ep0_in.dep = ep_priv;
>>> +
>>> +                   if (!epnum)
>>> +                           usbssp_data->gadget.ep0 = &ep_priv->endpoint;
>>> +           } else {
>>> +                   usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
>>> +                   ep_priv->endpoint.maxburst = 15;
>>> +                   ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
>>> +                   list_add_tail(&ep_priv->endpoint.ep_list,
>>> +                                   &usbssp_data->gadget.ep_list);
>>> +                   ep_priv->endpoint.caps.type_iso = true;
>>> +                   ep_priv->endpoint.caps.type_bulk = true;
>>> +                   ep_priv->endpoint.caps.type_int = true;
>>> +
>>> +           }
>>> +
>>> +           ep_priv->endpoint.caps.dir_in = direction;
>>> +           ep_priv->endpoint.caps.dir_out = !direction;
>>
>> Since you start from 1, ep0 will only be initialized as dir_in.
>> Is it better to represent EP0in and EP0out separately?
>> If so you can start i from 0 and use
>>      ep_priv = &usbssp_data->devs.eps[i];
>>
>> This should fix the direction. eps[0] will be ep0out and eps[1] will be 
>> ep0in.
> 
> I wanted to be consistent with Device Context Data Structure from 
> XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN 
> ...).
> 
> I little change this function, and now it look like this:
> int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data)
> {
>       int i = 0;
>       struct usbssp_ep *ep_priv;
> 
>       usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
>       INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
> 
>       for (i = 0; i < usbssp_data->num_endpoints; i++) {
>               bool direction = i & 1; /*start from OUT endpoint*/
>               u8 epnum = ((i + 1) >> 1);
> 
>               ep_priv = &usbssp_data->devs.eps[i];
>               ep_priv->usbssp_data = usbssp_data;
>               ep_priv->number = epnum;
>               ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
> 
>               /*
>                *Ep0 is bidirectional so Ep0In and Ep0Out is represented by
>                * usbssp_data->devs.eps[0]
>                */
>               if (epnum == 0) {
>                       snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
>                               epnum, "BiDir");
> 
>                       usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
>                       ep_priv->endpoint.maxburst = 1;
>                       ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
>                       ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
> 
>                       ep_priv->endpoint.comp_desc = NULL;
>                       ep_priv->endpoint.caps.type_control = true;
>                       ep_priv->endpoint.caps.dir_in = true;
>                       ep_priv->endpoint.caps.dir_out = true;
> 
>                       usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
>                       usbssp_data->usb_req_ep0_in.dep = ep_priv;
>                       usbssp_data->gadget.ep0 = &ep_priv->endpoint;
> 
>               } else {
>                       snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
>                               epnum, (ep_priv->direction) ? "in"  : "out");
> 
>                       usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
>                       ep_priv->endpoint.maxburst = 15;
>                       ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
>                       list_add_tail(&ep_priv->endpoint.ep_list,
>                                       &usbssp_data->gadget.ep_list);
> 
>                       ep_priv->endpoint.caps.type_iso = true;
>                       ep_priv->endpoint.caps.type_bulk = true;
>                       ep_priv->endpoint.caps.type_int = true;
> 
>                       ep_priv->endpoint.caps.dir_in = direction;
>                       ep_priv->endpoint.caps.dir_out = !direction;
>               }
> 
>               ep_priv->endpoint.name = ep_priv->name;

I think this is much better now.

> 
>               dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: "
>                       "CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
>                       "SupDir IN: %s, OUT: %s\n",
>                       ep_priv->name, 1024,
>                       (ep_priv->endpoint.caps.type_control) ? "yes" : "no",
>                       (ep_priv->endpoint.caps.type_int) ? "yes" : "no",
>                       (ep_priv->endpoint.caps.type_bulk) ? "yes" : "no",
>                       (ep_priv->endpoint.caps.type_iso) ? "yes" : "no",
>                       (ep_priv->endpoint.caps.dir_in) ? "yes" : "no",
>                       (ep_priv->endpoint.caps.dir_out) ? "yes" : "no");
> 

In some places you use usbssp_dbg_trace() and at others you use dev_dbg().
Is it better to stick with one throughout?

>               INIT_LIST_HEAD(&ep_priv->pending_list);
>       }
> 
>       return 0;
> }
> 
>>> +
>>> +           dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
>>> +                           " INT/BULK/ISOC , SupDir %s\n",
>>> +                           ep_priv->name, 1024,
>>> +                           (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
>>> +
>>> +           INIT_LIST_HEAD(&ep_priv->pending_list);
>>> +   }
>>>     return 0;
>>>  }
>>>
>>>  void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
>>>  {
>>> -   /*TODO: it has to be implemented*/
>>> +   int i;
>>> +   struct usbssp_ep *ep_priv;
>>> +
>>> +   for (i = 0; i < usbssp_data->num_endpoints; i++) {
>>> +           ep_priv = &usbssp_data->devs.eps[i];
>>> +
>>
>> if you start i from 1 then you can skip the if().
>>
>>> +           if (ep_priv->number != 0)
>>> +                   list_del(&ep_priv->endpoint.ep_list);
>>> +   }
>>>  }
>>> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
>>> index 338ec2ec18b1..195f5777cf8a 100644
>>> --- a/drivers/usb/usbssp/gadget.c
>>> +++ b/drivers/usb/usbssp/gadget.c
>>> @@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
>>>     return ret;
>>>  }
>>>
>>> +/*
>>> + * Set the run bit and wait for the device to be running.
>>> + */
>>> +int usbssp_start(struct usbssp_udc *usbssp_data)
>>> +{
>>> +   u32 temp;
>>> +   int ret;
>>> +
>>> +   temp = readl(&usbssp_data->op_regs->command);
>>> +   temp |= (CMD_RUN | CMD_DEVEN);
>>> +   usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
>>> +                   "// Turn on USBSSP, cmd = 0x%x.", temp);
>>> +   writel(temp, &usbssp_data->op_regs->command);
>>> +
>>> +   /*
>>> +    * Wait for the HCHalted Staus bit to be 0 to indicate the device is
>>> +    * running.
>>> +    */
>>> +   ret = usbssp_handshake(&usbssp_data->op_regs->status,
>>> +                   STS_HALT, 0, USBSSP_MAX_HALT_USEC);
>>> +
>>> +   if (ret == -ETIMEDOUT)
>>> +           dev_err(usbssp_data->dev, "Device took too long to start, 
>>> waited %u microseconds.\n",
>>> +                   USBSSP_MAX_HALT_USEC);
>>> +   if (!ret)
>>> +           /* clear state flags. Including dying, halted or removing */
>>> +           usbssp_data->usbssp_state = 0;
>>> +
>>> +   return ret;
>>> +}
>>> +
>>> +/*
>>> + * Reset a halted DC.
>>> + *
>>> + * This resets pipelines, timers, counters, state machines, etc.
>>> + * Transactions will be terminated immediately, and operational registers
>>> + * will be set to their defaults.
>>> + */
>>> +int usbssp_reset(struct usbssp_udc *usbssp_data)
>>> +{
>>> +   u32 command;
>>> +   u32 state;
>>> +   int ret;
>>> +
>>> +   state = readl(&usbssp_data->op_regs->status);
>>> +
>>> +   if (state == ~(u32)0) {
>>> +           dev_warn(usbssp_data->dev, "Device not accessible, reset 
>>> failed.\n");
>>> +           return -ENODEV;
>>> +   }
>>> +
>>> +   if ((state & STS_HALT) == 0) {
>>> +           dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");
>>
>> DC is not a familiar abbreviation. Mabe just use Controller? or Device 
>> controller.
> 
> In xhci driver is often used HC for host controller. In device driver I 
> change this abbreviation 
> to  DC. I will review the patches and change this to Controller.  
> 
>>
>>> +           return 0;
>>> +   }
>>> +
>>> +   usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
>>> +   command = readl(&usbssp_data->op_regs->command);
>>> +   command |= CMD_RESET;
>>> +   writel(command, &usbssp_data->op_regs->command);
>>> +
>>> +   ret = usbssp_handshake(&usbssp_data->op_regs->command,
>>> +                   CMD_RESET, 0, 10 * 1000 * 1000);
>>> +
>>> +   if (ret)
>>> +           return ret;
>>> +   usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
>>> +           "Wait for controller to be ready for doorbell rings");
>>> +   /*
>>> +    * USBSSP cannot write to any doorbells or operational registers other
>>> +    * than status until the "Controller Not Ready" flag is cleared.
>>> +    */
>>> +   ret = usbssp_handshake(&usbssp_data->op_regs->status,
>>> +                   STS_CNR, 0, 10 * 1000 * 1000);
>>> +
>>> +   return ret;
>>> +}
>>>
>>>  /*
>>>   * Initialize memory for gadget driver and USBSSP (one-time init).
>>> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
>>>
>>>     dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
>>>     /* Reset the internal DC memory state and registers. */
>>> -   /*TODO: add implementation of usbssp_reset function*/
>>> -   //retval = usbssp_reset(usbssp_data);
>>> +   retval = usbssp_reset(usbssp_data);
>>>     if (retval)
>>>             return retval;
>>>     dev_dbg(usbssp_data->dev, "Reset complete\n");
>>> @@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
>>>     BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
>>>
>>>     /* fill gadget fields */
>>> -   /*TODO: implements usbssp_gadget_ops object*/
>>> -   //usbssp_data->gadget.ops = &usbssp_gadget_ops;
>>> +   usbssp_data->gadget.ops = &usbssp_gadget_ops;
>>>     usbssp_data->gadget.name = "usbssp-gadget";
>>>     usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
>>>     usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
>>> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
>>>     usbssp_halt(usbssp_data);
>>>     /*TODO add implementation of usbssp_reset function*/
>>>     //usbssp_reset(usbssp_data);
>>> +   usbssp_reset(usbssp_data);
>>>     /*TODO add implementation of freeing memory*/
>>>     //usbssp_mem_cleanup(usbssp_data);
>>>  err3:
>>>
>>
>> --
>> cheers,
>> -roger
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
> 
> Cheers
> Pawel
> 

-- 
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

Reply via email to